diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index bfc4cf98..726d8e07 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -37,6 +37,7 @@ jobs: - large-house - isometric - plan-snap + - interaction - blend - overlay - space-default @@ -190,6 +191,10 @@ jobs: npm run benchmark:large-house-plan-snap -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/plan-snap-baseline.json npm run benchmark:large-house-plan-snap -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/plan-snap-candidate.json ;; + interaction) + npm run benchmark:large-house-interaction -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/interaction-baseline.json + npm run benchmark:large-house-interaction -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/interaction-candidate.json + ;; blend) npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/blend-baseline.json npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/blend-candidate.json @@ -235,6 +240,9 @@ jobs: plan-snap) npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-plan-snap.json --baseline=../artifacts/performance/plan-snap-baseline.json --candidate=../artifacts/performance/plan-snap-candidate.json --output=../artifacts/performance/plan-snap-comparison.json ;; + interaction) + npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-interaction.json --baseline=../artifacts/performance/interaction-baseline.json --candidate=../artifacts/performance/interaction-candidate.json --output=../artifacts/performance/interaction-comparison.json + ;; blend) npm run benchmark:compare -- --budgets=demo/performance/budgets-large-light-blend.json --baseline=../artifacts/performance/blend-baseline.json --candidate=../artifacts/performance/blend-candidate.json --output=../artifacts/performance/blend-comparison.json ;; diff --git a/custom_components/houseplan/frontend/houseplan-assets.json b/custom_components/houseplan/frontend/houseplan-assets.json index 99bc1ffe..b344cb35 100644 --- a/custom_components/houseplan/frontend/houseplan-assets.json +++ b/custom_components/houseplan/frontend/houseplan-assets.json @@ -1,169 +1,182 @@ { "schema": 1, - "fingerprint": "7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91", + "fingerprint": "71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7", "entry": "houseplan-card.js", "initialViewFiles": [ - "houseplan-assets/houseplan-card-DcH_msOh.js", + "houseplan-assets/houseplan-card-Csq_DR2Y.js", "houseplan-card.js" ], - "initialViewGzipBytes": 295744, + "initialViewGzipBytes": 296722, "lazyFiles": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/de-CcfVGZcf.js", - "houseplan-assets/editor-CrMXGx3s.js", - "houseplan-assets/fr-OCiOh_q7.js", - "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js", - "houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js", - "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/de-DR62x0IQ.js", + "houseplan-assets/editor-BVOuJXsz.js", + "houseplan-assets/fr-D4YIJQhT.js", + "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js", + "houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js", + "houseplan-assets/live-interaction-runtime-C2JvTP_O.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js", + "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js" ], - "lazyGzipBytes": 234162, + "lazyGzipBytes": 240676, "lazyEditorFiles": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/editor-CrMXGx3s.js", - "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/editor-BVOuJXsz.js", + "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js" ], - "lazyEditorGzipBytes": 173958, + "lazyEditorGzipBytes": 178334, "lazyOnboardingFiles": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js" ], - "lazyOnboardingGzipBytes": 14235, + "lazyOnboardingGzipBytes": 14241, "lazyLocaleFiles": [ - "houseplan-assets/de-CcfVGZcf.js", - "houseplan-assets/fr-OCiOh_q7.js" + "houseplan-assets/de-DR62x0IQ.js", + "houseplan-assets/fr-D4YIJQhT.js" ], - "lazyLocaleGzipBytes": 49109, + "lazyLocaleGzipBytes": 49115, "files": [ { - "path": "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "sha256": "ede625d42274e8806716776cb0e4de9707fa6713bd990f13c564fc9348fe08c5", - "rawBytes": 21418, + "path": "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "sha256": "709ac3421f7630a539c5c1c27248b39f76e6d9d2b54b75057abb0b4af70bc844", + "rawBytes": 21417, "gzipBytes": 7282, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/de-CcfVGZcf.js", - "sha256": "ceca4bfbade91448d50b8ce35e0e894fcf58023f692d6ecc11968add4a501661", + "path": "houseplan-assets/de-DR62x0IQ.js", + "sha256": "0377277d00b6e025e587aa7404a3cecea12c56d873fa9741680a4ab246914d09", "rawBytes": 82182, - "gzipBytes": 24824, + "gzipBytes": 24827, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/editor-CrMXGx3s.js", - "sha256": "70f2f6ee3de165adf92a553886464362fdfcad7dc99e294340e945d82369ece9", - "rawBytes": 3826, - "gzipBytes": 1579, + "path": "houseplan-assets/editor-BVOuJXsz.js", + "sha256": "efde2f9d2f8bc1f5fb961bb3c57feb8a291018f2871c4c068ab60bae58106446", + "rawBytes": 3831, + "gzipBytes": 1583, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/fr-OCiOh_q7.js", - "sha256": "35feec4d0e40f8d157537b89c32e7cf066fdb984c020c2112303b6863bfa2bfd", + "path": "houseplan-assets/fr-D4YIJQhT.js", + "sha256": "e5c0ff025e2826a8a43b53e6af22c7314a5b72effd2b41d7cfd38a128f998925", "rawBytes": 84312, - "gzipBytes": 24285, + "gzipBytes": 24288, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-card-DcH_msOh.js", - "sha256": "3ad91eaf921e0d682e4b9c14af09d754d1511b432624cc5e7064d71b00aabe0e", - "rawBytes": 1050357, - "gzipBytes": 294949, + "path": "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "sha256": "2751d48b52d10030532b2de87f0f0c7f38c283223bae57c8728b2717b115c353", + "rawBytes": 1053904, + "gzipBytes": 295925, "isEntry": false, "imports": [], "dynamicImports": [ - "houseplan-assets/de-CcfVGZcf.js", - "houseplan-assets/editor-CrMXGx3s.js", - "houseplan-assets/fr-OCiOh_q7.js", - "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js", - "houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js" + "houseplan-assets/de-DR62x0IQ.js", + "houseplan-assets/editor-BVOuJXsz.js", + "houseplan-assets/fr-D4YIJQhT.js", + "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js", + "houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js", + "houseplan-assets/live-interaction-runtime-C2JvTP_O.js" ] }, { - "path": "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "sha256": "9045a38e1be7f4a93f6934c82380b94e2e9df95532bcfd2085d25c5225a00a04", - "rawBytes": 608951, - "gzipBytes": 160252, + "path": "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "sha256": "db3ca36d567fb682fb633555bdfa963db0d2f5e9682c1532bdf6ef81c17591fc", + "rawBytes": 622491, + "gzipBytes": 164622, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/houseplan-card-DcH_msOh.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js" ], "dynamicImports": [ - "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js" + "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js" ] }, { - "path": "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js", - "sha256": "1619296b625e2dd17d93ab4bc01e928dec730ddf5b8a56e12a7bc276addf10e6", + "path": "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js", + "sha256": "bb156e1a1244328a728875141ddf47d2ebe0d1e12e763eb1336e2f72837bcfaf", "rawBytes": 28088, - "gzipBytes": 6953, + "gzipBytes": 6959, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js", - "sha256": "4e87b71be6179c03adb570887921e1a86ba8965483be24135f3bce2d27a5ed82", - "rawBytes": 6007, - "gzipBytes": 2380, + "path": "houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js", + "sha256": "aac582f8d2e0586aeed749ff9581f1fb7d11bb15493cc939bd37b61f903c05aa", + "rawBytes": 6006, + "gzipBytes": 2376, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js", - "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js", + "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js", - "sha256": "dfd3a3dfc807b9035019ea89187327f20975ad11ff0635f1c9df4ad542d3a6f6", - "rawBytes": 3941, - "gzipBytes": 1762, + "path": "houseplan-assets/live-interaction-runtime-C2JvTP_O.js", + "sha256": "91e8a03ebb3fb35b89c974e2bcf3ee1eff8523f9d9cb1ac93a061c3f751706d1", + "rawBytes": 5196, + "gzipBytes": 2128, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/zigbee-topology-x104QxRs.js", - "sha256": "d52ca29eeb5a62e0a00362844d00c66258fef6c375e090b7173515eb78ccf856", + "path": "houseplan-assets/zigbee-topology-D9L1ESh4.js", + "sha256": "817e5b42008db12cdf922275098eb06a536408dbd01f464859dccee434c2451b", "rawBytes": 12382, - "gzipBytes": 4845, + "gzipBytes": 4847, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" + ], + "dynamicImports": [] + }, + { + "path": "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js", + "sha256": "8197d48ba2f6eb615024f0cabca6ff12c10a3442fa19ebcd536bd5d3d5586c34", + "rawBytes": 3941, + "gzipBytes": 1764, + "isEntry": false, + "imports": [ + "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js" ], "dynamicImports": [] }, { "path": "houseplan-card.js", - "sha256": "e50d3f3c6e84d612ead3f39c6f39d987697dbea608e85d839a41848268cb73a4", + "sha256": "dbba45693954cf08e2165d88bdcb9e5c295d3782fe5fae9943e079e22135e22c", "rawBytes": 1183, - "gzipBytes": 795, + "gzipBytes": 797, "isEntry": true, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] } diff --git a/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-CXzP7hUG.js b/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-CEZBr4qx.js similarity index 59% rename from custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-CXzP7hUG.js rename to custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-CEZBr4qx.js index ca0aea1a..3e82be4e 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-CXzP7hUG.js +++ b/custom_components/houseplan/frontend/houseplan-assets/backdrop-pick-CEZBr4qx.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{dn as e,c as t,A as i,j as o,k as n,dp as r}from"./houseplan-card-DcH_msOh.js";function s(e){const t=e.visualViewport;return t&&Number.isFinite(t.width)&&Number.isFinite(t.height)?{left:Number(t.offsetLeft)||0,top:Number(t.offsetTop)||0,width:Math.max(0,Number(t.width)||0),height:Math.max(0,Number(t.height)||0)}:{left:0,top:0,width:e.innerWidth,height:e.innerHeight}}function a(e,t,i,o=7,n=8){const r=i.left+n,s=i.top+n,a=Math.max(r,i.left+i.width-n),l=Math.max(s,i.top+i.height-n),h=Math.max(0,a-r),c=Math.max(0,l-s),d=Math.min(Math.max(0,t.width),h),u=Math.min(Math.max(0,t.height),c);let p=e.left;p+d>a&&(p=e.right-d),p=Math.min(Math.max(r,p),Math.max(r,a-d));const g=e.bottom+o,_=e.top-o-u;let m="bottom",f=g;return g+u>l&&_>=s?(m="top",f=Math.min(Math.max(s,_),Math.max(s,l-u))):f=Math.min(Math.max(s,g),Math.max(s,l-u)),{left:Math.round(p),top:Math.round(f),side:m,maxWidth:Math.round(h),maxHeight:Math.round(c)}}class l{constructor(e,t,i){this.owner=e,this.name=t,this.keydown=i,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(e=!1){const t=this.window();return!e&&!!t&&function(e){const t=e.HTMLElement?.prototype;return"function"==typeof t?.showPopover&&"function"==typeof t?.hidePopover}(t)}get hasFallback(){return!!this.host}containsPath(e){return e.includes(this.owner)||!!this.host&&e.includes(this.host)}ownsActiveElement(){let e=this.owner.ownerDocument.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return!(!e||e!==this.owner&&!this.owner.shadowRoot?.contains(e)&&!this.root?.contains(e))}renderFallback(i,o){const n=this.ensureFallback();return n?(e(t`${i}`,n),n):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&e(i,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const e=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!e)return null;const t=this.owner.ownerDocument.createElement("div");return t.dataset.hpOverlay=this.name,t.style.display="contents",this.keydown&&t.addEventListener("keydown",this.keydown,!0),e.append(t),this.host=t,this.root=t.attachShadow({mode:"open"}),this.root}}let h=0;class c extends n{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++h,this._surfacePointerEnter=()=>{const e=this._window();this._closeTimer&&e?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closeHelp(!1,"outside")},this._dialogScroll=e=>{const t=this._floating.containsPath(e.composedPath()),i=e.target,o=this._dialog(),n=i instanceof Node&&(i===o||i instanceof Element&&i.contains(this));var r;this._open&&(r=n,!t&&r)&&this._closeHelp(!1,"scroll")},this._keyDown=e=>{if(!this._open||e.defaultPrevented)return;if("Escape"===e.key)return e.preventDefault(),e.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const t=this.renderRoot.querySelector(".trigger");if(!t||!e.composedPath().includes(t))return;const i=this._surface();if(!i||i.scrollHeight<=i.clientHeight)return;const o=Math.max(40,.8*i.clientHeight),n={ArrowDown:40,ArrowUp:-40,PageDown:o,PageUp:-o};if("Home"===e.key)i.scrollTop=0;else if("End"===e.key)i.scrollTop=i.scrollHeight;else{if(!(e.key in n))return;i.scrollBy({top:n[e.key],behavior:"auto"})}e.preventDefault(),e.stopImmediatePropagation()},this._floating=new l(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const e=this._window();e&&(this._positionRaf=e.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const e=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&e?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(e){if(e.has("pointerHover")&&!this.pointerHover){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&e.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return e=this.text,t=this.ariaLabel,"string"==typeof e&&e.trim().length>0&&"string"==typeof t&&t.trim().length>0;var e,t}_clearTimers(){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._closeTimer&&e?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const e=this._window();this.pointerHover&&e&&!this._open&&!this._openTimer&&(this._closeTimer&&e.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=e.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const e=this._window(),t=this.renderRoot.querySelector(".trigger");e&&!t?.matches(":focus-visible")&&(this._openTimer&&e.clearTimeout(this._openTimer),this._closeTimer&&e.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=e.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(e){this.pointerHover&&"mouse"===e.pointerType&&this._scheduleOpen()}_triggerPointerLeave(e){"mouse"===e.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const e=this.renderRoot.querySelector(".trigger");e?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const e=this._window();e?.addEventListener("resize",this._queuePosition),e?.addEventListener("orientationchange",this._queuePosition),e?.visualViewport?.addEventListener("resize",this._queuePosition),e?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const e=this._window();e?.removeEventListener("resize",this._queuePosition),e?.removeEventListener("orientationchange",this._queuePosition),e?.visualViewport?.removeEventListener("resize",this._queuePosition),e?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:e=>this._closeHelp("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closeHelp()))}_closeHelp(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const i=this._overlayDispose;this._overlayDispose=null,i?.();const o=this.renderRoot.querySelector(".tooltip");if(o?.hidePopover)try{o.matches(":popover-open")&&o.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(e){return t`
a&&(p=e.right-d),p=Math.min(Math.max(r,p),Math.max(r,a-d));const g=e.bottom+o,_=e.top-o-u;let m="bottom",f=g;return g+u>l&&_>=s?(m="top",f=Math.min(Math.max(s,_),Math.max(s,l-u))):f=Math.min(Math.max(s,g),Math.max(s,l-u)),{left:Math.round(p),top:Math.round(f),side:m,maxWidth:Math.round(h),maxHeight:Math.round(c)}}class l{constructor(e,t,i){this.owner=e,this.name=t,this.keydown=i,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(e=!1){const t=this.window();return!e&&!!t&&function(e){const t=e.HTMLElement?.prototype;return"function"==typeof t?.showPopover&&"function"==typeof t?.hidePopover}(t)}get hasFallback(){return!!this.host}containsPath(e){return e.includes(this.owner)||!!this.host&&e.includes(this.host)}ownsActiveElement(){let e=this.owner.ownerDocument.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return!(!e||e!==this.owner&&!this.owner.shadowRoot?.contains(e)&&!this.root?.contains(e))}renderFallback(i,o){const n=this.ensureFallback();return n?(e(t`${i}`,n),n):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&e(i,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const e=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!e)return null;const t=this.owner.ownerDocument.createElement("div");return t.dataset.hpOverlay=this.name,t.style.display="contents",this.keydown&&t.addEventListener("keydown",this.keydown,!0),e.append(t),this.host=t,this.root=t.attachShadow({mode:"open"}),this.root}}let h=0;class c extends n{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++h,this._surfacePointerEnter=()=>{const e=this._window();this._closeTimer&&e?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closeHelp(!1,"outside")},this._dialogScroll=e=>{const t=this._floating.containsPath(e.composedPath()),i=e.target,o=this._dialog(),n=i instanceof Node&&(i===o||i instanceof Element&&i.contains(this));var r;this._open&&(r=n,!t&&r)&&this._closeHelp(!1,"scroll")},this._keyDown=e=>{if(!this._open||e.defaultPrevented)return;if("Escape"===e.key)return e.preventDefault(),e.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const t=this.renderRoot.querySelector(".trigger");if(!t||!e.composedPath().includes(t))return;const i=this._surface();if(!i||i.scrollHeight<=i.clientHeight)return;const o=Math.max(40,.8*i.clientHeight),n={ArrowDown:40,ArrowUp:-40,PageDown:o,PageUp:-o};if("Home"===e.key)i.scrollTop=0;else if("End"===e.key)i.scrollTop=i.scrollHeight;else{if(!(e.key in n))return;i.scrollBy({top:n[e.key],behavior:"auto"})}e.preventDefault(),e.stopImmediatePropagation()},this._floating=new l(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const e=this._window();e&&(this._positionRaf=e.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const e=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&e?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(e){if(e.has("pointerHover")&&!this.pointerHover){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&e.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return e=this.text,t=this.ariaLabel,"string"==typeof e&&e.trim().length>0&&"string"==typeof t&&t.trim().length>0;var e,t}_clearTimers(){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._closeTimer&&e?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const e=this._window();this.pointerHover&&e&&!this._open&&!this._openTimer&&(this._closeTimer&&e.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=e.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const e=this._window(),t=this.renderRoot.querySelector(".trigger");e&&!t?.matches(":focus-visible")&&(this._openTimer&&e.clearTimeout(this._openTimer),this._closeTimer&&e.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=e.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(e){this.pointerHover&&"mouse"===e.pointerType&&this._scheduleOpen()}_triggerPointerLeave(e){"mouse"===e.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const e=this.renderRoot.querySelector(".trigger");e?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const e=this._window();e?.addEventListener("resize",this._queuePosition),e?.addEventListener("orientationchange",this._queuePosition),e?.visualViewport?.addEventListener("resize",this._queuePosition),e?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const e=this._window();e?.removeEventListener("resize",this._queuePosition),e?.removeEventListener("orientationchange",this._queuePosition),e?.visualViewport?.removeEventListener("resize",this._queuePosition),e?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:e=>this._closeHelp("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closeHelp()))}_closeHelp(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const i=this._overlayDispose;this._overlayDispose=null,i?.();const o=this.renderRoot.querySelector(".tooltip");if(o?.hidePopover)try{o.matches(":popover-open")&&o.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(e){return t``}_renderFallback(){const e=c.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),e)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const e=this._window(),t=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!e||!t?.isConnected||!i?.isConnected)return!1;const o=s(e);if(i.style.maxWidth=`${Math.max(0,Math.min(320,o.width-16))}px`,i.style.maxHeight=`${Math.max(0,o.height-16)}px`,i.style.visibility="hidden",this._usesPopover()&&i.showPopover)try{i.matches(":popover-open")||i.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),i=this._surface(),!i?.isConnected)return!1;i.style.visibility="hidden"}const n=t.getBoundingClientRect(),r=i.getBoundingClientRect();if(!(n.width&&n.height&&r.width&&r.height))return!1;const l=a(n,r,o);return i.style.left=`${l.left}px`,i.style.top=`${l.top}px`,i.dataset.side=l.side,i.style.visibility="",!0}render(){if(!this._hasContent())return i;const e=this.ariaLabel.trim();return t` ({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`h[e.name]||e.name} + @value-changed=${this._valueChanged} + >`;return c` + ${d(f.slice(0,3))} + ${_?c``:a} + ${d(f.slice(3))} + `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h); diff --git a/custom_components/houseplan/frontend/houseplan-assets/editor-CrMXGx3s.js b/custom_components/houseplan/frontend/houseplan-assets/editor-CrMXGx3s.js deleted file mode 100644 index 94d7ad4c..00000000 --- a/custom_components/houseplan/frontend/houseplan-assets/editor-CrMXGx3s.js +++ /dev/null @@ -1,14 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{k as e,l as o,t,dq as s,A as a,dr as i,ds as l,dt as r,E as n,c}from"./houseplan-card-DcH_msOh.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const r="string"==typeof this._config?.default_floor?this._config.default_floor:"",n=[...e];return r&&!e.some(e=>e.value===r)&&n.unshift({value:r,label:r}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:n}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return r();if("warm"===e)return n;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`h[e.name]||e.name} - @value-changed=${this._valueChanged} - >`;return c` - ${d(f.slice(0,3))} - ${_?c``:a} - ${d(f.slice(3))} - `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h); diff --git a/dist/houseplan-assets/fr-OCiOh_q7.js b/custom_components/houseplan/frontend/houseplan-assets/fr-D4YIJQhT.js similarity index 99% rename from dist/houseplan-assets/fr-OCiOh_q7.js rename to custom_components/houseplan/frontend/houseplan-assets/fr-D4YIJQhT.js index 3ed3c3d2..b2f635dd 100644 --- a/dist/houseplan-assets/fr-OCiOh_q7.js +++ b/custom_components/houseplan/frontend/houseplan-assets/fr-D4YIJQhT.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft_title":"Supprimer le contour inachevé ?","confirm.delete_draft_body":"Tout le contour de pièce inachevé sera supprimé. L’annulation ne modifie rien.","confirm.delete_draft_segment_title":"Supprimer le segment de contour ?","confirm.delete_draft_segment_body":"Ce segment de brouillon sera supprimé. Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","history.device_move":"Déplacer {name}","history.device_stale":"L’historique des positions n’est plus applicable et a été effacé","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker_title":"Supprimer l’appareil du plan ?","confirm.remove_marker_body":"L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space_title":"Supprimer l’espace ?","confirm.delete_space_body":"L’espace, toutes ses pièces et tout le tracé du plan seront supprimés.","confirm.delete_space_vac_routes":"Les affectations de cartes de robots seront aussi supprimées : {count}.","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.cell_cm.help":"Relie la grille aux dimensions réelles : les longueurs, les surfaces, l’épaisseur des murs, la taille des ouvertures et le rayon du halo en dépendent. Une modification ultérieure change les dimensions calculées de tout l’espace sans déplacer ses points sur le plan.","space.cell_cm.help.aria":"Aide : échelle de l’espace","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_style.help":"Les murs d’épaisseur nulle en pointillés laissent passer le halo et les rayons du soleil, tandis que les murs pleins les bloquent comme une barrière sans surface. Ce choix s’applique à tous ces murs de l’espace, même si leur ligne est masquée en mode Vue.","space.zero_wall_style.help.aria":"Aide : style des murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","space.fill_mode.help":"« Couleur personnalisée » définit le style, tandis que « Lumière », « Température » et « LQI » utilisent les données actuelles de Home Assistant. Le halo s’active séparément.","space.fill_mode.help.aria":"Aide : mode de remplissage des pièces","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.show_hidden.help":"Affiche temporairement les marqueurs masqués et désactivés sous forme de fantômes réservés à l’éditeur. La visibilité enregistrée des appareils ne change pas.","device_inbox.show_hidden.help.aria":"Aide : afficher les appareils masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration « {integration} » exclue par les filtres de découverte","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","device_inbox.filters_excluded":"Intégrations exclues","device_inbox.filters_group_lights":"Regrouper les lumières de la pièce en un marqueur","device_inbox.filters_preview_appear":"apparaîtra : {count}","device_inbox.filters_preview_hide":"disparaîtra : {count}","device_inbox.filters_preview_lights":"lumières concernées : {count}","device_inbox.filters_reset":"Rétablir les recommandées","device_inbox.filters_save":"Enregistrer les filtres","device_inbox.filters_search_ph":"Ajouter une intégration…","device_inbox.filters_title":"Filtres de découverte","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.flip_h":"Retourner horizontalement","decor.flip_v":"Retourner verticalement","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.glow_radius.help":"Définit le rayon général du halo en mètres ou en pieds ; le rayon propre à un appareil remplace cette valeur.","gs.glow_radius.help.aria":"Aide : rayon général du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls.help":"Avec l’action « Basculer l’état », les sources répertoriées basculent avec ce marqueur. Leur halo reste sur leurs propres marqueurs et ce lien ne transforme pas à lui seul le marqueur de contrôle en source lumineuse.","marker.controls.help.aria":"Aide : contrôler d’autres sources lumineuses","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","room.fit_action":"Ajuster la pièce {name}","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.value_source":"Source de la valeur","marker.value_source.help":"Choisissez la valeur qui remplace l’icône. L’action au toucher ne change pas.","marker.value_source.help.aria":"Aide : source de la valeur dans le marqueur","marker.value_source_auto":"Automatique (comme avant)","marker.value_source_missing_hint":"La source enregistrée est indisponible. Le marqueur affiche — jusqu’à son retour ou jusqu’au choix d’une autre valeur.","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"« Auto » utilise le rôle déterminé de l’appareil lié, « Toujours » force sa propre source et « Jamais » l’exclut ; les lumières liées ci-dessus restent indépendantes.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock_title":"Déverrouiller ?","confirm.unlock_body":"House Plan enverra une commande de déverrouillage à cette serrure.","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan_title":"Supprimer le fichier du plan ?","confirm.delete_plan_body":"Le fichier sera supprimé du serveur. Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","tap.none":"Ne rien faire","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.route_warn_unmapped":"Le robot nettoie une carte qui n'est assignée à aucun étage","vac.route_warn_needs_calibration":"La carte est assignée à un étage mais n'est pas calibrée","vac.route_warn_ambiguous":"Deux cartes sont indiscernables — aucun étage choisi","vac.route_warn_missing_space":"L'espace de cette carte a été supprimé","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_mode.help":"« Suit le soleil » utilise sun.sun ou, à défaut, l’heure locale du navigateur ; « Couleur statique » affiche toujours la couleur choisie.","gs.bg_mode.help.aria":"Aide : arrière-plan général du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north.help":"L’angle se mesure dans le sens horaire depuis la verticale supérieure du plan ; le nord sert aux rayons des fenêtres, pas à l’arrière-plan « Suit le soleil ».","gs.north.help.aria":"Aide : direction générale du nord","gs.north_ph":"non défini","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","support.title":"Aide et commentaires","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.bg_mode.help":"Héritez de l’arrière-plan général ou remplacez-le pour cet espace par une couleur statique ou le mode « Suit le soleil ».","space.bg_mode.help.aria":"Aide : arrière-plan de l’espace","space.north":"Nord sur le plan (remplacement)","space.north.help":"Remplace le nord général pour cet espace ; l’angle se mesure dans le sens horaire depuis la verticale supérieure du plan et sert aux rayons des fenêtres.","space.north.help.aria":"Aide : nord de cet espace","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","decor.image":"Image","decor.image_title":"Images personnalisées","decor.image_upload":"Importer une image","decor.image_replace":"Remplacer l’image","decor.image_uploading":"Importation…","decor.image_asset":"Fichier image","decor.image_pick_hint":"Importez une image ou choisissez-en une existante.","decor.image_place_hint":"Cliquez sur le plan pour placer l’image.","decor.image_none":"Aucune image importée pour le moment.","decor.image_unavailable":"Image indisponible","decor.image_used":"Utilisée dans {n} objets","decor.image_upload_failed":"Impossible d’importer l’image : {err}","decor.image_error_capacity":"Le stockage des images personnalisées est plein.","decor.image_in_use":"Supprimez toutes les copies placées avant ce fichier.","decor.image_delete_title":"Supprimer le fichier image ?","decor.image_delete_message":"Supprimer « {name} » de House Plan ? Cette action est irréversible.","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.support_invalid_message":"Vérifiez le message d’assistance et réessayez.","backup.error.support_package_too_large":"Le paquet d’assistance anonymisé dépasse la limite de 8 Mio.","backup.error.support_preview_expired":"L’aperçu de la pièce jointe d’assistance a expiré ou n’est plus disponible.","backup.error.support_rate_limited":"Trop de rapports ou d’aperçus d’assistance ont été demandés. Réessayez plus tard.","backup.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","backup.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.invalid_image":"L’image est endommagée ou ne correspond pas à son format.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.unsupported_image":"Utilisez une image PNG, JPEG, WebP ou SVG prise en charge.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.error.in_use":"L'élément est encore utilisé","backup.error.invalid_data":"La requête contient des données invalides","backup.error.invalid_light_entity":"La lumière principale doit être une entité light.* ou switch.*","backup.error.invalid_marker_control":"Référence de contrôle de marqueur invalide","backup.error.invalid_name":"Le nom est invalide","backup.error.invalid_partition_opening_host":"L'ouverture doit conserver sa cloison porteuse","backup.error.invalid_partition_opening_jamb_margin":"L'ouverture ne laisse aucun jambage physique au bout du mur","backup.error.invalid_passage_fields":"Un passage ouvert ne peut pas porter de réglages de porte","backup.error.invalid_space_id":"Espace inconnu","backup.error.invalid_toggle_entity":"L'entité de bascule doit être light.* ou switch.*","backup.error.invalid_value_badge":"Réglages de badge de valeur invalides","backup.error.invalid_value_badge_attribute":"Attribut de badge de valeur invalide","backup.error.invalid_value_badge_position":"Position de badge de valeur invalide","backup.error.invalid_value_badge_source":"Source de badge de valeur invalide","backup.error.invalid_vacuum_map_route":"Vérifiez l'affectation des cartes du robot aux espaces.","backup.error.invalid_value_source":"Source de valeur invalide","backup.error.invalid_value_source_attribute":"Attribut de source de valeur invalide","backup.error.io_error":"Une opération de fichier a échoué sur le serveur","backup.error.not_toggleable":"Cet appareil ne peut pas être basculé","backup.error.nothing_to_repair":"Rien à réparer","backup.error.space_in_use":"L'espace est encore référencé","backup.error.value_badge_source_required":"Le badge de valeur nécessite une source","backup.error.wall_model_client_outdated":"Le plan a été mis à jour ailleurs — rechargez la page","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.decor_images_summary":"Images personnalisées : {objects} objets, {assets} fichiers ; fichiers absents : {missing}.","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.confirm_missing_images":"Je comprends que les images personnalisées absentes resteront des espaces réservés réparables et que les autres fichiers locaux indisponibles seront dissociés.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.reduced_dimensions":"Copie réduite : {w}×{h} px.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const r="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";export{e as dictionary,r as fingerprint}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft_title":"Supprimer le contour inachevé ?","confirm.delete_draft_body":"Tout le contour de pièce inachevé sera supprimé. L’annulation ne modifie rien.","confirm.delete_draft_segment_title":"Supprimer le segment de contour ?","confirm.delete_draft_segment_body":"Ce segment de brouillon sera supprimé. Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","history.device_move":"Déplacer {name}","history.device_stale":"L’historique des positions n’est plus applicable et a été effacé","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker_title":"Supprimer l’appareil du plan ?","confirm.remove_marker_body":"L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space_title":"Supprimer l’espace ?","confirm.delete_space_body":"L’espace, toutes ses pièces et tout le tracé du plan seront supprimés.","confirm.delete_space_vac_routes":"Les affectations de cartes de robots seront aussi supprimées : {count}.","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.cell_cm.help":"Relie la grille aux dimensions réelles : les longueurs, les surfaces, l’épaisseur des murs, la taille des ouvertures et le rayon du halo en dépendent. Une modification ultérieure change les dimensions calculées de tout l’espace sans déplacer ses points sur le plan.","space.cell_cm.help.aria":"Aide : échelle de l’espace","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_style.help":"Les murs d’épaisseur nulle en pointillés laissent passer le halo et les rayons du soleil, tandis que les murs pleins les bloquent comme une barrière sans surface. Ce choix s’applique à tous ces murs de l’espace, même si leur ligne est masquée en mode Vue.","space.zero_wall_style.help.aria":"Aide : style des murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","space.fill_mode.help":"« Couleur personnalisée » définit le style, tandis que « Lumière », « Température » et « LQI » utilisent les données actuelles de Home Assistant. Le halo s’active séparément.","space.fill_mode.help.aria":"Aide : mode de remplissage des pièces","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.show_hidden.help":"Affiche temporairement les marqueurs masqués et désactivés sous forme de fantômes réservés à l’éditeur. La visibilité enregistrée des appareils ne change pas.","device_inbox.show_hidden.help.aria":"Aide : afficher les appareils masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration « {integration} » exclue par les filtres de découverte","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","device_inbox.filters_excluded":"Intégrations exclues","device_inbox.filters_group_lights":"Regrouper les lumières de la pièce en un marqueur","device_inbox.filters_preview_appear":"apparaîtra : {count}","device_inbox.filters_preview_hide":"disparaîtra : {count}","device_inbox.filters_preview_lights":"lumières concernées : {count}","device_inbox.filters_reset":"Rétablir les recommandées","device_inbox.filters_save":"Enregistrer les filtres","device_inbox.filters_search_ph":"Ajouter une intégration…","device_inbox.filters_title":"Filtres de découverte","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.flip_h":"Retourner horizontalement","decor.flip_v":"Retourner verticalement","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.glow_radius.help":"Définit le rayon général du halo en mètres ou en pieds ; le rayon propre à un appareil remplace cette valeur.","gs.glow_radius.help.aria":"Aide : rayon général du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls.help":"Avec l’action « Basculer l’état », les sources répertoriées basculent avec ce marqueur. Leur halo reste sur leurs propres marqueurs et ce lien ne transforme pas à lui seul le marqueur de contrôle en source lumineuse.","marker.controls.help.aria":"Aide : contrôler d’autres sources lumineuses","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","room.fit_action":"Ajuster la pièce {name}","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.value_source":"Source de la valeur","marker.value_source.help":"Choisissez la valeur qui remplace l’icône. L’action au toucher ne change pas.","marker.value_source.help.aria":"Aide : source de la valeur dans le marqueur","marker.value_source_auto":"Automatique (comme avant)","marker.value_source_missing_hint":"La source enregistrée est indisponible. Le marqueur affiche — jusqu’à son retour ou jusqu’au choix d’une autre valeur.","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"« Auto » utilise le rôle déterminé de l’appareil lié, « Toujours » force sa propre source et « Jamais » l’exclut ; les lumières liées ci-dessus restent indépendantes.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock_title":"Déverrouiller ?","confirm.unlock_body":"House Plan enverra une commande de déverrouillage à cette serrure.","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan_title":"Supprimer le fichier du plan ?","confirm.delete_plan_body":"Le fichier sera supprimé du serveur. Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","tap.none":"Ne rien faire","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.route_warn_unmapped":"Le robot nettoie une carte qui n'est assignée à aucun étage","vac.route_warn_needs_calibration":"La carte est assignée à un étage mais n'est pas calibrée","vac.route_warn_ambiguous":"Deux cartes sont indiscernables — aucun étage choisi","vac.route_warn_missing_space":"L'espace de cette carte a été supprimé","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_mode.help":"« Suit le soleil » utilise sun.sun ou, à défaut, l’heure locale du navigateur ; « Couleur statique » affiche toujours la couleur choisie.","gs.bg_mode.help.aria":"Aide : arrière-plan général du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north.help":"L’angle se mesure dans le sens horaire depuis la verticale supérieure du plan ; le nord sert aux rayons des fenêtres, pas à l’arrière-plan « Suit le soleil ».","gs.north.help.aria":"Aide : direction générale du nord","gs.north_ph":"non défini","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","support.title":"Aide et commentaires","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.bg_mode.help":"Héritez de l’arrière-plan général ou remplacez-le pour cet espace par une couleur statique ou le mode « Suit le soleil ».","space.bg_mode.help.aria":"Aide : arrière-plan de l’espace","space.north":"Nord sur le plan (remplacement)","space.north.help":"Remplace le nord général pour cet espace ; l’angle se mesure dans le sens horaire depuis la verticale supérieure du plan et sert aux rayons des fenêtres.","space.north.help.aria":"Aide : nord de cet espace","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","decor.image":"Image","decor.image_title":"Images personnalisées","decor.image_upload":"Importer une image","decor.image_replace":"Remplacer l’image","decor.image_uploading":"Importation…","decor.image_asset":"Fichier image","decor.image_pick_hint":"Importez une image ou choisissez-en une existante.","decor.image_place_hint":"Cliquez sur le plan pour placer l’image.","decor.image_none":"Aucune image importée pour le moment.","decor.image_unavailable":"Image indisponible","decor.image_used":"Utilisée dans {n} objets","decor.image_upload_failed":"Impossible d’importer l’image : {err}","decor.image_error_capacity":"Le stockage des images personnalisées est plein.","decor.image_in_use":"Supprimez toutes les copies placées avant ce fichier.","decor.image_delete_title":"Supprimer le fichier image ?","decor.image_delete_message":"Supprimer « {name} » de House Plan ? Cette action est irréversible.","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.support_invalid_message":"Vérifiez le message d’assistance et réessayez.","backup.error.support_package_too_large":"Le paquet d’assistance anonymisé dépasse la limite de 8 Mio.","backup.error.support_preview_expired":"L’aperçu de la pièce jointe d’assistance a expiré ou n’est plus disponible.","backup.error.support_rate_limited":"Trop de rapports ou d’aperçus d’assistance ont été demandés. Réessayez plus tard.","backup.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","backup.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.invalid_image":"L’image est endommagée ou ne correspond pas à son format.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.unsupported_image":"Utilisez une image PNG, JPEG, WebP ou SVG prise en charge.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.error.in_use":"L'élément est encore utilisé","backup.error.invalid_data":"La requête contient des données invalides","backup.error.invalid_light_entity":"La lumière principale doit être une entité light.* ou switch.*","backup.error.invalid_marker_control":"Référence de contrôle de marqueur invalide","backup.error.invalid_name":"Le nom est invalide","backup.error.invalid_partition_opening_host":"L'ouverture doit conserver sa cloison porteuse","backup.error.invalid_partition_opening_jamb_margin":"L'ouverture ne laisse aucun jambage physique au bout du mur","backup.error.invalid_passage_fields":"Un passage ouvert ne peut pas porter de réglages de porte","backup.error.invalid_space_id":"Espace inconnu","backup.error.invalid_toggle_entity":"L'entité de bascule doit être light.* ou switch.*","backup.error.invalid_value_badge":"Réglages de badge de valeur invalides","backup.error.invalid_value_badge_attribute":"Attribut de badge de valeur invalide","backup.error.invalid_value_badge_position":"Position de badge de valeur invalide","backup.error.invalid_value_badge_source":"Source de badge de valeur invalide","backup.error.invalid_vacuum_map_route":"Vérifiez l'affectation des cartes du robot aux espaces.","backup.error.invalid_value_source":"Source de valeur invalide","backup.error.invalid_value_source_attribute":"Attribut de source de valeur invalide","backup.error.io_error":"Une opération de fichier a échoué sur le serveur","backup.error.not_toggleable":"Cet appareil ne peut pas être basculé","backup.error.nothing_to_repair":"Rien à réparer","backup.error.space_in_use":"L'espace est encore référencé","backup.error.value_badge_source_required":"Le badge de valeur nécessite une source","backup.error.wall_model_client_outdated":"Le plan a été mis à jour ailleurs — rechargez la page","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.decor_images_summary":"Images personnalisées : {objects} objets, {assets} fichiers ; fichiers absents : {missing}.","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.confirm_missing_images":"Je comprends que les images personnalisées absentes resteront des espaces réservés réparables et que les autres fichiers locaux indisponibles seront dissociés.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.reduced_dimensions":"Copie réduite : {w}×{h} px.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const r="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";export{e as dictionary,r as fingerprint}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-DcH_msOh.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-Csq_DR2Y.js similarity index 63% rename from custom_components/houseplan/frontend/houseplan-assets/houseplan-card-DcH_msOh.js rename to custom_components/houseplan/frontend/houseplan-assets/houseplan-card-Csq_DR2Y.js index 47d30e8e..35ab8836 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-DcH_msOh.js +++ b/custom_components/houseplan/frontend/houseplan-assets/houseplan-card-Csq_DR2Y.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,R="?"+C,T=`<${R}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,F="[ \t\n\f\r]",O=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,N=RegExp(`>|${F}(?:([^\\s"'>=/]+)(${F}*=${F}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=O;for(let t=0;t"===l[0]?(s=r??O,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?N:'"'===l[3]?L:H):s===L||s===H?s=N:s===I||s===E?s=O:(s=N,r=void 0);const d=s===N&&e[t+1].startsWith("/>")?" ":"";o+=s===O?i+T:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.alert=!1,this.describedBy="",this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._usesHaDialog()?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._usesHaDialog())return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}_usesHaDialog(){return this._useHaDialog&&!this.alert}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._usesHaDialog()){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}rejectClose(){this._closing=!1,this.requestUpdate()}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=W` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,S=k.trustedTypes,$=S?S.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,R="?"+C,T=`<${R}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,F="[ \t\n\f\r]",O=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,N=RegExp(`>|${F}(?:([^\\s"'>=/]+)(${F}*=${F}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==$?$.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=O;for(let t=0;t"===l[0]?(s=r??O,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?N:'"'===l[3]?L:H):s===L||s===H?s=N:s===I||s===E?s=O:(s=N,r=void 0);const d=s===N&&e[t+1].startsWith("/>")?" ":"";o+=s===O?i+T:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=S?S.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.alert=!1,this.describedBy="",this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._usesHaDialog()?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._usesHaDialog())return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}_usesHaDialog(){return this._useHaDialog&&!this.alert}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._usesHaDialog()){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}rejectClose(){this._closing=!1,this.requestUpdate()}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=W` ${this.icon?W``:V} ${this.title} `;return this._usesHaDialog()?this.describedBy?W`{this._active={...t,resolve:e},this._changed(t)})}resolve(e,t){const i=this._active;return!(!i||i.token!==e)&&(this._active=null,this._changed(null),i.resolve(t),!0)}cancel(){const e=this._active;return!!e&&this.resolve(e.token,!1)}}const fe=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),ve=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|thermometer|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function ye(e){const t=[];for(const i of e)if(i&&"string"==typeof i.pattern&&i.icon)try{t.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return t}function be(e){try{return new RegExp(e,"i"),!0}catch{return!1}}const we=ye(ve),ke={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",presence:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},xe="mdi:chip";function $e(e,t,i){const n=((e||"")+" "+(t||"")).toLowerCase();for(const{re:e,icon:t}of i??we)if(e.test(n))return t;return xe}const Se=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var Me=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Ce=Math.ceil,Re=Math.floor,Te="[BigNumber Error] ",De=Te+"Number primitive has more than 15 significant digits: ",ze=1e14,Ae=14,Pe=9007199254740991,Fe=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Oe=1e7,Ie=1e9;function Ee(e){var t=0|e;return e>0||e===t?t:t-1}function Ne(e){for(var t,i,n=1,r=e.length,o=e[0]+"";nc^i?1:-1;for(a=(l=r.length)<(c=o.length)?l:c,s=0;so[s]^i?1:-1;return l==c?0:l>c^i?1:-1}function Le(e,t,i,n){if(ei||e!==Re(e))throw Error(Te+(n||"Argument")+("number"==typeof e?ei?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function Be(e){var t=e.c.length-1;return Ee(e.e/Ae)==t&&e.c[t]%2!=0}function qe(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function We(e,t,i){var n,r;if(t<0){for(r=i+".";++t;r+=i);e=r+e}else if(++t>(n=e.length)){for(r=i,t-=n;--t;r+=i);e+=r}else ty?u.c=u.e=null:e.e=10;l/=10,a++);return void(a>y?u.c=u.e=null:(u.e=a,u.c=[e]))}d=String(e)}else{if(!Me.test(d=String(e)))return r(u,d,c);u.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Le(t,2,$.length,"Base"),10==t&&S)return D(u=new M(e),m+u.e+1,_);if(d=String(e),c="number"==typeof e){if(0*e!=0)return r(u,d,c,t);if(u.s=1/e<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(De+e)}else u.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,t),a=l=0,h=d.length;la){a=h;continue}}else if(!s&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){s=!0,l=-1,a=0;continue}return r(u,String(e),c,t)}c=!1,(a=(d=n(d,t,10,u.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(e>Pe||e!==Re(e)))throw Error(De+u.s*e);if((a=a-l-1)>y)u.c=u.e=null;else if(a=f)?qe(l,s):We(l,s,"0");else if(o=(e=D(new M(e),t,i)).e,a=(l=Ne(e.c)).length,1==n||2==n&&(t<=o||o<=g)){for(;as),l=We(l,o,"0"),o+1>a){if(--t>0)for(l+=".";t--;l+="0");}else if((t+=o-a)>0)for(o+1==a&&(l+=".");t--;l+="0");return e.s<0&&r?"-"+l:l}function R(e,t){for(var i,n,r=1,o=new M(e[0]);r=10;r/=10,n++);return(i=n+i*Ae-1)>y?e.c=e.e=null:i=10;a/=10,r++);if((o=t-r)<0)o+=Ae,s=t,l=d[c=0],h=Re(l/u[r-s-1]%10);else if((c=Ce((o+1)/Ae))>=d.length){if(!n)break e;for(;d.length<=c;d.push(0));l=h=0,r=1,s=(o%=Ae)-Ae+1}else{for(l=a=d[c],r=1;a>=10;a/=10,r++);h=(s=(o%=Ae)-Ae+r)<0?0:Re(l/u[r-s-1]%10)}if(n=n||t<0||null!=d[c+1]||(s<0?l:l%u[r-s-1]),n=i<4?(h||n)&&(0==i||i==(e.s<0?3:2)):h>5||5==h&&(4==i||n||6==i&&(o>0?s>0?l/u[r-s]:0:d[c-1])%10&1||i==(e.s<0?8:7)),t<1||!d[0])return d.length=0,n?(t-=e.e+1,d[0]=u[(Ae-t%Ae)%Ae],e.e=-t||0):d[0]=e.e=0,e;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=u[Ae-o],d[c]=s>0?Re(l/u[r-s]%u[s])*a:0),n)for(;;){if(0==c){for(o=1,s=d[0];s>=10;s/=10,o++);for(s=d[0]+=a,a=1;s>=10;s/=10,a++);o!=a&&(e.e++,d[0]==ze&&(d[0]=1));break}if(d[c]+=a,d[c]!=ze)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}e.e>y?e.c=e.e=null:e.e=f?qe(t,i):We(t,i,"0"),e.s<0?"-"+t:t)}return M.clone=e,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(e){var t,i;if(null!=e){if("object"!=typeof e)throw Error(Te+"Object expected: "+e);if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(Le(i=e[t],0,Ie,t),m=i),e.hasOwnProperty(t="ROUNDING_MODE")&&(Le(i=e[t],0,8,t),_=i),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((i=e[t])&&i.pop?(Le(i[0],-Ie,0,t),Le(i[1],0,Ie,t),g=i[0],f=i[1]):(Le(i,-Ie,Ie,t),g=-(f=i<0?-i:i))),e.hasOwnProperty(t="RANGE"))if((i=e[t])&&i.pop)Le(i[0],-Ie,-1,t),Le(i[1],1,Ie,t),v=i[0],y=i[1];else{if(Le(i,-Ie,Ie,t),!i)throw Error(Te+t+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(e.hasOwnProperty(t="CRYPTO")){if((i=e[t])!==!!i)throw Error(Te+t+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(Te+"crypto unavailable");b=i}else b=i}if(e.hasOwnProperty(t="MODULO_MODE")&&(Le(i=e[t],0,9,t),w=i),e.hasOwnProperty(t="POW_PRECISION")&&(Le(i=e[t],0,Ie,t),k=i),e.hasOwnProperty(t="FORMAT")){if("object"!=typeof(i=e[t]))throw Error(Te+t+" not an object: "+i);x=i}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(i=e[t])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(Te+t+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:m,ROUNDING_MODE:_,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!M.DEBUG)return!0;var t,i,n=e.c,r=e.e,o=e.s;e:if("[object Array]"=={}.toString.call(n)){if((1===o||-1===o)&&r>=-Ie&&r<=Ie&&r===Re(r)){if(0===n[0]){if(0===r&&1===n.length)return!0;break e}if((t=(r+1)%Ae)<1&&(t+=Ae),String(n[0]).length==t){for(t=0;t=ze||i!==Re(i))break e;if(0!==i)return!0}}}else if(null===n&&null===r&&(null===o||1===o||-1===o))return!0;throw Error(Te+"Invalid BigNumber: "+e)},M.maximum=M.max=function(){return R(arguments,-1)},M.minimum=M.min=function(){return R(arguments,1)},M.random=(o=9007199254740992,s=Math.random()*o&2097151?function(){return Re(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(e){var t,i,n,r,o,a=0,l=[],c=new M(p);if(null==e?e=m:Le(e,0,Ie),r=Ce(e/Ae),b)if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(r*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),t[a]=i[0],t[a+1]=i[1]):(l.push(o%1e14),a+=2);a=r/2}else{if(!crypto.randomBytes)throw b=!1,Error(Te+"crypto unavailable");for(t=crypto.randomBytes(r*=7);a=9e15?crypto.randomBytes(7).copy(t,a):(l.push(o%1e14),a+=7);a=r/7}if(!b)for(;a=10;o/=10,a++);ai-1&&(null==s[r+1]&&(s[r+1]=0),s[r+1]+=s[r]/i|0,s[r]%=i)}return s.reverse()}return function(n,r,o,s,a){var l,c,h,d,u,p,g,f,v=n.indexOf("."),y=m,b=_;for(v>=0&&(d=k,k=0,n=n.replace(".",""),p=(f=new M(r)).pow(n.length-v),k=d,f.c=t(We(Ne(p.c),p.e,"0"),10,o,e),f.e=f.c.length),h=d=(g=t(n,r,o,a?(l=$,e):(l=e,$))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(p.c=g,p.e=h,p.s=s,g=(p=i(p,f,y,b,o)).c,u=p.r,h=p.e),v=g[c=h+y+1],d=o/2,u=u||c<0||null!=g[c+1],u=b<4?(null!=v||u)&&(0==b||b==(p.s<0?3:2)):v>d||v==d&&(4==b||u||6==b&&1&g[c-1]||b==(p.s<0?8:7)),c<1||!g[0])n=u?We(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,u)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,n="";v<=d;n+=l.charAt(g[v++]));n=We(n,h,l.charAt(0))}return n}}(),i=function(){function e(e,t,i){var n,r,o,s,a=0,l=e.length,c=t%Oe,h=t/Oe|0;for(e=e.slice();l--;)a=((r=c*(o=e[l]%Oe)+(n=h*o+(s=e[l]/Oe|0)*c)%Oe*Oe+a)/i|0)+(n/Oe|0)+h*s,e[l]=r%i;return a&&(e=[a].concat(e)),e}function t(e,t,i,n){var r,o;if(i!=n)o=i>n?1:-1;else for(r=o=0;rt[r]?1:-1;break}return o}function i(e,t,i,n){for(var r=0;i--;)e[i]-=r,r=e[i]1;e.splice(0,1));}return function(n,r,o,s,a){var l,c,h,d,u,p,m,_,g,f,v,y,b,w,k,x,$,S=n.s==r.s?1:-1,C=n.c,R=r.c;if(!(C&&C[0]&&R&&R[0]))return new M(n.s&&r.s&&(C?!R||C[0]!=R[0]:R)?C&&0==C[0]||!R?0*S:S/0:NaN);for(g=(_=new M(S)).c=[],S=o+(c=n.e-r.e)+1,a||(a=ze,c=Ee(n.e/Ae)-Ee(r.e/Ae),S=S/Ae|0),h=0;R[h]==(C[h]||0);h++);if(R[h]>(C[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=C.length,x=R.length,h=0,S+=2,(u=Re(a/(R[0]+1)))>1&&(R=e(R,u,a),C=e(C,u,a),x=R.length,w=C.length),b=x,v=(f=C.slice(0,x)).length;v=a/2&&k++;do{if(u=0,(l=t(R,f,x,v))<0){if(y=f[0],x!=v&&(y=y*a+(f[1]||0)),(u=Re(y/k))>1)for(u>=a&&(u=a-1),m=(p=e(R,u,a)).length,v=f.length;1==t(p,f,m,v);)u--,i(p,x=10;S/=10,h++);D(_,o+(_.e=h+c*Ae-1)+1,s,d)}else _.e=c,_.r=+d;return _}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,r=function(e,t,i,n){var r,o=i?t:t.replace(d,"");if(h.test(o))e.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(e,t,i){return r="x"==(i=i.toLowerCase())?16:"b"==i?2:8,n&&n!=r?e:t}),n&&(r=n,o=o.replace(l,"$1").replace(c,"0.$1")),t!=o))return new M(o,r);if(M.DEBUG)throw Error(Te+"Not a"+(n?" base "+n:"")+" number: "+t);e.s=null}e.c=e.e=null},u.absoluteValue=u.abs=function(){var e=new M(this);return e.s<0&&(e.s=1),e},u.comparedTo=function(e,t){return He(this,new M(e,t))},u.decimalPlaces=u.dp=function(e,t){var i,n,r,o=this;if(null!=e)return Le(e,0,Ie),null==t?t=_:Le(t,0,8),D(new M(o),e+o.e+1,t);if(!(i=o.c))return null;if(n=((r=i.length-1)-Ee(this.e/Ae))*Ae,r=i[r])for(;r%10==0;r/=10,n--);return n<0&&(n=0),n},u.dividedBy=u.div=function(e,t){return i(this,new M(e,t),m,_)},u.dividedToIntegerBy=u.idiv=function(e,t){return i(this,new M(e,t),0,1)},u.exponentiatedBy=u.pow=function(e,t){var i,n,r,o,s,a,l,c,h=this;if((e=new M(e)).c&&!e.isInteger())throw Error(Te+"Exponent not an integer: "+z(e));if(null!=t&&(t=new M(t)),s=e.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!e.c||!e.c[0])return c=new M(Math.pow(+z(h),s?e.s*(2-Be(e)):+z(e))),t?c.mod(t):c;if(a=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new M(NaN);(n=!a&&h.isInteger()&&t.isInteger())&&(h=h.mod(t))}else{if(e.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||s&&h.c[1]>=24e7:h.c[0]<8e13||s&&h.c[0]<=9999975e7)))return o=h.s<0&&Be(e)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=Ce(k/Ae+2))}for(s?(i=new M(.5),a&&(e.s=1),l=Be(e)):l=(r=Math.abs(+z(e)))%2,c=new M(p);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):n&&(c=c.mod(t))}if(r){if(0===(r=Re(r/2)))break;l=r%2}else if(D(e=e.times(i),e.e+1,1),e.e>14)l=Be(e);else{if(0===(r=+z(e)))break;l=r%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):n&&(h=h.mod(t))}return n?c:(a&&(c=p.div(c)),t?c.mod(t):o?D(c,k,_,void 0):c)},u.integerValue=function(e){var t=new M(this);return null==e?e=_:Le(e,0,8),D(t,t.e+1,e)},u.isEqualTo=u.eq=function(e,t){return 0===He(this,new M(e,t))},u.isFinite=function(){return!!this.c},u.isGreaterThan=u.gt=function(e,t){return He(this,new M(e,t))>0},u.isGreaterThanOrEqualTo=u.gte=function(e,t){return 1===(t=He(this,new M(e,t)))||0===t},u.isInteger=function(){return!!this.c&&Ee(this.e/Ae)>this.c.length-2},u.isLessThan=u.lt=function(e,t){return He(this,new M(e,t))<0},u.isLessThanOrEqualTo=u.lte=function(e,t){return-1===(t=He(this,new M(e,t)))||0===t},u.isNaN=function(){return!this.s},u.isNegative=function(){return this.s<0},u.isPositive=function(){return this.s>0},u.isZero=function(){return!!this.c&&0==this.c[0]},u.minus=function(e,t){var i,n,r,o,s=this,a=s.s;if(t=(e=new M(e,t)).s,!a||!t)return new M(NaN);if(a!=t)return e.s=-t,s.plus(e);var l=s.e/Ae,c=e.e/Ae,h=s.c,d=e.c;if(!l||!c){if(!h||!d)return h?(e.s=-t,e):new M(d?s:NaN);if(!h[0]||!d[0])return d[0]?(e.s=-t,e):new M(h[0]?s:3==_?-0:0)}if(l=Ee(l),c=Ee(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,r=h):(c=l,r=d),r.reverse(),t=a;t--;r.push(0));r.reverse()}else for(n=(o=(a=h.length)<(t=d.length))?a:t,a=t=0;t0)for(;t--;h[i++]=0);for(t=ze-1;n>a;){if(h[--n]=0;){for(i=0,u=y[r]%g,p=y[r]/g|0,o=r+(s=l);o>r;)i=((c=u*(c=v[--s]%g)+(a=p*c+(h=v[s]/g|0)*u)%g*g+m[o]+i)/_|0)+(a/g|0)+p*h,m[o--]=c%_;m[o]=i}return i?++n:m.splice(0,1),T(e,m,n)},u.negated=function(){var e=new M(this);return e.s=-e.s||null,e},u.plus=function(e,t){var i,n=this,r=n.s;if(t=(e=new M(e,t)).s,!r||!t)return new M(NaN);if(r!=t)return e.s=-t,n.minus(e);var o=n.e/Ae,s=e.e/Ae,a=n.c,l=e.c;if(!o||!s){if(!a||!l)return new M(r/0);if(!a[0]||!l[0])return l[0]?e:new M(a[0]?n:0*r)}if(o=Ee(o),s=Ee(s),a=a.slice(),r=o-s){for(r>0?(s=o,i=l):(r=-r,i=a),i.reverse();r--;i.push(0));i.reverse()}for((r=a.length)-(t=l.length)<0&&(i=l,l=a,a=i,t=r),r=0;t;)r=(a[--t]=a[t]+l[t]+r)/ze|0,a[t]=ze===a[t]?0:a[t]%ze;return r&&(a=[r].concat(a),++s),T(e,a,s)},u.precision=u.sd=function(e,t){var i,n,r,o=this;if(null!=e&&e!==!!e)return Le(e,1,Ie),null==t?t=_:Le(t,0,8),D(new M(o),e,t);if(!(i=o.c))return null;if(n=(r=i.length-1)*Ae+1,r=i[r]){for(;r%10==0;r/=10,n--);for(r=i[0];r>=10;r/=10,n++);}return e&&o.e+1>n&&(n=o.e+1),n},u.shiftedBy=function(e){return Le(e,-9007199254740991,Pe),this.times("1e"+e)},u.squareRoot=u.sqrt=function(){var e,t,n,r,o,s=this,a=s.c,l=s.s,c=s.e,h=m+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?s:1/0);if(0==(l=Math.sqrt(+z(s)))||l==1/0?(((t=Ne(a)).length+c)%2==0&&(t+="0"),l=Math.sqrt(+t),c=Ee((c+1)/2)-(c<0||c%2),n=new M(t=l==1/0?"5e"+c:(t=l.toExponential()).slice(0,t.indexOf("e")+1)+c)):n=new M(l+""),n.c[0])for((l=(c=n.e)+h)<3&&(l=0);;)if(o=n,n=d.times(o.plus(i(s,o,h,1))),Ne(o.c).slice(0,l)===(t=Ne(n.c)).slice(0,l)){if(n.e0&&m>0){for(o=m%a||a,h=p.substr(0,o);o0&&(h+=c+p.slice(o)),u&&(h="-"+h)}n=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+n+(i.suffix||"")},u.toFraction=function(e){var t,n,r,o,s,a,l,c,h,d,u,m,g=this,f=g.c;if(null!=e&&(!(l=new M(e)).isInteger()&&(l.c||1!==l.s)||l.lt(p)))throw Error(Te+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+z(l));if(!f)return new M(g);for(t=new M(p),h=n=new M(p),r=c=new M(p),m=Ne(f),s=t.e=m.length-g.e-1,t.c[0]=Fe[(a=s%Ae)<0?Ae+a:a],e=!e||l.comparedTo(t)>0?s>0?t:h:l,a=y,y=1/0,l=new M(m),c.c[0]=0;d=i(l,t,0,1),1!=(o=n.plus(d.times(r))).comparedTo(e);)n=r,r=o,h=c.plus(d.times(o=h)),c=o,t=l.minus(d.times(o=t)),l=o;return o=i(e.minus(n),r,0,1),c=c.plus(o.times(h)),n=n.plus(o.times(r)),c.s=h.s=g.s,u=i(h,r,s*=2,_).minus(g).abs().comparedTo(i(c,n,s,_).minus(g).abs())<1?[h,r]:[c,n],y=a,u},u.toNumber=function(){return+z(this)},u.toPrecision=function(e,t){return null!=e&&Le(e,1,Ie),C(this,e,t,2)},u.toString=function(e){var t,i=this,r=i.s,o=i.e;return null===o?r?(t="Infinity",r<0&&(t="-"+t)):t="NaN":(null==e?t=o<=g||o>=f?qe(Ne(i.c),o):We(Ne(i.c),o,"0"):10===e&&S?t=We(Ne((i=D(new M(i),m+o+1,_)).c),i.e,"0"):(Le(e,2,$.length,"Base"),t=n(We(Ne(i.c),o,"0"),10,e,r,!0)),r<0&&i.c[0]&&(t="-"+t)),t},u.valueOf=u.toJSON=function(){return z(this)},u._isBigNumber=!0,u[Symbol.toStringTag]="BigNumber",u[Symbol.for("nodejs.util.inspect.custom")]=u.valueOf,null!=t&&M.set(t),M}(),Ue=class{key;left=null;right=null;constructor(e){this.key=e}},Ve=class extends Ue{constructor(e){super(e)}},Ge=class{size=0;modificationCount=0;splayCount=0;splay(e){const t=this.root;if(null==t)return this.compare(e,e),-1;let i=null,n=null,r=null,o=null,s=t;const a=this.compare;let l;for(;;)if(l=a(s.key,e),l>0){let t=s.left;if(null==t)break;if(l=a(t.key,e),l>0&&(s.left=t.right,t.right=s,s=t,t=s.left,null==t))break;null==i?n=s:i.left=s,i=s,s=t}else{if(!(l<0))break;{let t=s.right;if(null==t)break;if(l=a(t.key,e),l<0&&(s.right=t.left,t.left=s,s=t,t=s.right,null==t))break;null==r?o=s:r.right=s,r=s,s=t}}return null!=r&&(r.right=s.left,s.left=o),null!=i&&(i.left=s.right,s.right=n),this.root!==s&&(this.root=s,this.splayCount++),l}splayMin(e){let t=e,i=t.left;for(;null!=i;){const e=i;t.left=e.right,e.right=t,t=e,i=t.left}return t}splayMax(e){let t=e,i=t.right;for(;null!=i;){const e=i;t.right=e.left,e.left=t,t=e,i=t.right}return t}_delete(e){if(null==this.root)return null;if(0!=this.splay(e))return null;let t=this.root;const i=t,n=t.left;if(this.size--,null==n)this.root=t.right;else{const e=t.right;t=this.splayMax(n),t.right=e,this.root=t}return this.modificationCount++,i}addNewRoot(e,t){this.size++,this.modificationCount++;const i=this.root;null!=i?(t<0?(e.left=i,e.right=i.right,i.right=null):(e.right=i,e.left=i.left,i.left=null),this.root=e):this.root=e}_first(){const e=this.root;return null==e?null:(this.root=this.splayMin(e),this.root)}_last(){const e=this.root;return null==e?null:(this.root=this.splayMax(e),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(e){return this.validKey(e)&&0==this.splay(e)}defaultCompare(){return(e,t)=>et?1:0}wrap(){return{getRoot:()=>this.root,setRoot:e=>{this.root=e},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:e=>{this.splayCount=e},splay:e=>this.splay(e),has:e=>this.has(e)}}},Ke=class e extends Ge{root=null;compare;validKey;constructor(e,t){super(),this.compare=e??this.defaultCompare(),this.validKey=t??(e=>null!=e&&null!=e)}delete(e){return!!this.validKey(e)&&null!=this._delete(e)}deleteAll(e){for(const t of e)this.delete(t)}forEach(e){const t=this[Symbol.iterator]();let i;for(;i=t.next(),!i.done;)e(i.value,i.value,this)}add(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new Ve(e),t),this}addAndReturn(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new Ve(e),t),this.root.key}addAll(e){for(const t of e)this.add(t)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)<0)return this.root.key;let t=this.root.left;if(null==t)return null;let i=t.right;for(;null!=i;)t=i,i=t.right;return t.key}firstAfter(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)>0)return this.root.key;let t=this.root.right;if(null==t)return null;let i=t.left;for(;null!=i;)t=i,i=t.left;return t.key}retainAll(t){const i=new e(this.compare,this.validKey),n=this.modificationCount;for(const e of t){if(n!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(e)&&0==this.splay(e)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(e){if(!this.validKey(e))return null;return 0!=this.splay(e)?null:this.root.key}intersection(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)&&i.add(e);return i}difference(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)||i.add(e);return i}union(e){const t=this.clone();return t.addAll(e),t}clone(){const t=new e(this.compare,this.validKey);return t.size=this.size,t.root=this.copyNode(this.root),t}copyNode(e){if(null==e)return null;const t=new Ve(e.key);return function e(t,i){let n,r;do{if(n=t.left,r=t.right,null!=n){const t=new Ve(n.key);i.left=t,e(n,t)}if(null!=r){const e=new Ve(r.key);i.right=e,t=r,i=e}}while(null!=r)}(e,t),t}toSet(){return this.clone()}entries(){return new Ze(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Xe(this.wrap())}[Symbol.toStringTag]="[object Set]"},Ye=class{tree;path=new Array;modificationCount=null;splayCount;constructor(e){this.tree=e,this.splayCount=e.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const e=this.path[this.path.length-1];return this.getValue(e)}rebuildPath(e){this.path.splice(0,this.path.length),this.tree.splay(e),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(e){for(;null!=e;)this.path.push(e),e=e.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let e=this.tree.getRoot();for(;null!=e;)this.path.push(e),e=e.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let e=this.path[this.path.length-1],t=e.right;if(null!=t){for(;null!=t;)this.path.push(t),t=t.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===e;)e=this.path.pop();return this.path.length>0}},Xe=class extends Ye{getValue(e){return e.key}},Ze=class extends Ye{getValue(e){return[e.key,e.key]}},Je=e=>()=>e,Qe=e=>{const t=e?(t,i)=>i.minus(t).abs().isLessThanOrEqualTo(e):Je(!1);return(e,i)=>t(e,i)?0:e.comparedTo(i)};function et(e){const t=e?(t,i,n,r,o)=>t.exponentiatedBy(2).isLessThanOrEqualTo(r.minus(i).exponentiatedBy(2).plus(o.minus(n).exponentiatedBy(2)).times(e)):Je(!1);return(e,i,n)=>{const r=e.x,o=e.y,s=n.x,a=n.y,l=o.minus(a).times(i.x.minus(s)).minus(r.minus(s).times(i.y.minus(a)));return t(l,r,o,s,a)?0:l.comparedTo(0)}}var tt=e=>e,it=e=>{if(e){const t=new Ke(Qe(e)),i=new Ke(Qe(e)),n=(e,t)=>t.addAndReturn(e),r=e=>({x:n(e.x,t),y:n(e.y,i)});return r({x:new je(0),y:new je(0)}),r}return tt},nt=e=>({set:e=>{rt=nt(e)},reset:()=>nt(e),compare:Qe(e),snap:it(e),orient:et(e)}),rt=nt(),ot=(e,t)=>e.ll.x.isLessThanOrEqualTo(t.x)&&t.x.isLessThanOrEqualTo(e.ur.x)&&e.ll.y.isLessThanOrEqualTo(t.y)&&t.y.isLessThanOrEqualTo(e.ur.y),st=(e,t)=>{if(t.ur.x.isLessThan(e.ll.x)||e.ur.x.isLessThan(t.ll.x)||t.ur.y.isLessThan(e.ll.y)||e.ur.y.isLessThan(t.ll.y))return null;const i=e.ll.x.isLessThan(t.ll.x)?t.ll.x:e.ll.x,n=e.ur.x.isLessThan(t.ur.x)?e.ur.x:t.ur.x;return{ll:{x:i,y:e.ll.y.isLessThan(t.ll.y)?t.ll.y:e.ll.y},ur:{x:n,y:e.ur.y.isLessThan(t.ur.y)?e.ur.y:t.ur.y}}},at=(e,t)=>e.x.times(t.y).minus(e.y.times(t.x)),lt=(e,t)=>e.x.times(t.x).plus(e.y.times(t.y)),ct=e=>lt(e,e).sqrt(),ht=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return at(r,n).div(ct(r)).div(ct(n))},dt=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return lt(r,n).div(ct(r)).div(ct(n))},ut=(e,t,i)=>t.y.isZero()?null:{x:e.x.plus(t.x.div(t.y).times(i.minus(e.y))),y:i},pt=(e,t,i)=>t.x.isZero()?null:{x:i,y:e.y.plus(t.y.div(t.x).times(i.minus(e.x)))},mt=class e{point;isLeft;segment;otherSE;consumedBy;static compare(t,i){const n=e.comparePoints(t.point,i.point);return 0!==n?n:(t.point!==i.point&&t.link(i),t.isLeft!==i.isLeft?t.isLeft?1:-1:kt.compare(t.segment,i.segment))}static comparePoints(e,t){return e.x.isLessThan(t.x)?-1:e.x.isGreaterThan(t.x)?1:e.y.isLessThan(t.y)?-1:e.y.isGreaterThan(t.y)?1:0}constructor(e,t){void 0===e.events?e.events=[this]:e.events.push(this),this.point=e,this.isLeft=t}link(e){if(e.point===this.point)throw new Error("Tried to link already linked events");const t=e.point.events;for(let e=0,i=t.length;e{const n=i.otherSE;t.set(i,{sine:ht(this.point,e.point,n.point),cosine:dt(this.point,e.point,n.point)})};return(e,n)=>{t.has(e)||i(e),t.has(n)||i(n);const{sine:r,cosine:o}=t.get(e),{sine:s,cosine:a}=t.get(n);return r.isGreaterThanOrEqualTo(0)&&s.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:r.isLessThan(0)&&s.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:s.isLessThan(r)?-1:s.isGreaterThan(r)?1:0}}},_t=class e{events;poly;_isExteriorRing;_enclosingRing;static factory(t){const i=[];for(let n=0,r=t.length;n0&&(e=i)}let t=e.segment.prevInResult(),i=t?t.prevInResult():null;for(;;){if(!t)return null;if(!i)return t.ringOut;if(i.ringOut!==t.ringOut)return i.ringOut?.enclosingRing()!==t.ringOut?t.ringOut:t.ringOut?.enclosingRing();t=i.prevInResult(),i=t?t.prevInResult():null}}},gt=class{exteriorRing;interiorRings;constructor(e){this.exteriorRing=e,e.poly=this,this.interiorRings=[]}addInterior(e){this.interiorRings.push(e),e.poly=this}getGeom(){const e=this.exteriorRing.getGeom();if(null===e)return null;const t=[e];for(let e=0,i=this.interiorRings.length;e0?(this.tree.delete(t),i.push(e)):(this.segments.push(t),t.prev=n)}else{if(n&&r){const e=n.getIntersection(r);if(null!==e){if(!n.isAnEndpoint(e)){const t=this._splitSafely(n,e);for(let e=0,n=t.length;e0)return-1;const n=t.comparePoint(e.rightSE.point);return 0!==n?n:-1}if(i.isGreaterThan(n)){if(s.isLessThan(a)&&s.isLessThan(c))return-1;if(s.isGreaterThan(a)&&s.isGreaterThan(c))return 1;const i=t.comparePoint(e.leftSE.point);if(0!==i)return i;const n=e.comparePoint(t.rightSE.point);return n<0?1:n>0?-1:1}if(s.isLessThan(a))return-1;if(s.isGreaterThan(a))return 1;if(r.isLessThan(o)){const i=t.comparePoint(e.rightSE.point);if(0!==i)return i}if(r.isGreaterThan(o)){const i=e.comparePoint(t.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!r.eq(o)){const e=l.minus(s),t=r.minus(i),h=c.minus(a),d=o.minus(n);if(e.isGreaterThan(t)&&h.isLessThan(d))return 1;if(e.isLessThan(t)&&h.isGreaterThan(d))return-1}return r.isGreaterThan(o)?1:r.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:e.idt.id?1:0}constructor(e,t,i,n){this.id=++wt,this.leftSE=e,e.segment=this,e.otherSE=t,this.rightSE=t,t.segment=this,t.otherSE=e,this.rings=i,this.windings=n}static fromRing(t,i,n){let r,o,s;const a=mt.comparePoints(t,i);if(a<0)r=t,o=i,s=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${t.x}, ${t.y}]`);r=i,o=t,s=-1}const l=new mt(r,!0),c=new mt(o,!1);return new e(l,c,[n],[s])}replaceRightSE(e){this.rightSE=e,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const e=this.leftSE.point.y,t=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:e.isLessThan(t)?e:t},ur:{x:this.rightSE.point.x,y:e.isGreaterThan(t)?e:t}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(e){return e.x.eq(this.leftSE.point.x)&&e.y.eq(this.leftSE.point.y)||e.x.eq(this.rightSE.point.x)&&e.y.eq(this.rightSE.point.y)}comparePoint(e){return rt.orient(this.leftSE.point,e,this.rightSE.point)}getIntersection(e){const t=this.bbox(),i=e.bbox(),n=st(t,i);if(null===n)return null;const r=this.leftSE.point,o=this.rightSE.point,s=e.leftSE.point,a=e.rightSE.point,l=ot(t,s)&&0===this.comparePoint(s),c=ot(i,r)&&0===e.comparePoint(r),h=ot(t,a)&&0===this.comparePoint(a),d=ot(i,o)&&0===e.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&r.x.eq(a.x)&&r.y.eq(a.y)?null:r;if(l)return d&&o.x.eq(s.x)&&o.y.eq(s.y)?null:s;if(d&&h)return null;if(d)return o;if(h)return a;const u=((e,t,i,n)=>{if(t.x.isZero())return pt(i,n,e.x);if(n.x.isZero())return pt(e,t,i.x);if(t.y.isZero())return ut(i,n,e.y);if(n.y.isZero())return ut(e,t,i.y);const r=at(t,n);if(r.isZero())return null;const o={x:i.x.minus(e.x),y:i.y.minus(e.y)},s=at(o,t).div(r),a=at(o,n).div(r),l=e.x.plus(a.times(t.x)),c=i.x.plus(s.times(n.x)),h=e.y.plus(a.times(t.y)),d=i.y.plus(s.times(n.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(r,this.vector(),s,e.vector());return null===u?null:ot(n,u)?rt.snap(u):null}split(t){const i=[],n=void 0!==t.events,r=new mt(t,!0),o=new mt(t,!1),s=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(r);const a=new e(r,s,this.rings.slice(),this.windings.slice());return mt.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),mt.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),n&&(r.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const e=this.rightSE;this.rightSE=this.leftSE,this.leftSE=e,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let e=0,t=this.windings.length;e0){const e=i;i=n,n=e}if(i.prev===n){const e=i;i=n,n=e}for(let e=0,t=n.rings.length;e1===e.length&&e[0].isSubject;this._isInResult=i(e)!==i(t);break}}return this._isInResult}},xt=class{poly;isExterior;segments;bbox;constructor(e,t,i){if(!Array.isArray(e)||0===e.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=t,this.isExterior=i,this.segments=[],"number"!=typeof e[0][0]||"number"!=typeof e[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const n=rt.snap({x:new je(e[0][0]),y:new je(e[0][1])});this.bbox={ll:{x:n.x,y:n.y},ur:{x:n.x,y:n.y}};let r=n;for(let t=1,i=e.length;tbt.run("union",e,t),Ct=(e,...t)=>bt.run("intersection",e,t),Rt=(e,...t)=>bt.run("difference",e,t);rt.set;const Tt=/^#[0-9a-fA-F]{6}$/;function Dt(e,t){return"string"==typeof e&&Tt.test(e)?e:t}const zt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",At=new RegExp(`^rgb\\(${zt}, ${zt}, ${zt}\\)$`);function Pt(e){if(!Array.isArray(e)||e.length<3)return null;const t=e.slice(0,3);if(!t.every(e=>"number"==typeof e&&Number.isFinite(e)))return null;const[i,n,r]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${n}, ${r})`}function Ft(e){const t=Math.max(0,Math.min(120,(e-40)/140*120));return`hsl(${Math.round(t)}, 85%, 55%)`}function Ot(e,t){if(!(Number.isFinite(e)&&t>0))return e;const i=Math.round(e/t)*t;return Math.abs(i-e)<=1e-9*t?e:i}function It(e,t,i,n=1/0){if(!(i>0&&e?.every(Number.isFinite)&&t?.every(Number.isFinite)))return[t[0],t[1]];const r=t[0]-e[0],o=t[1]-e[1];if(Math.abs(r)+Math.abs(o)<=1e-12)return[e[0],e[1]];const s=Math.PI/4;let a=Math.atan2(o,r);a<0&&(a+=2*Math.PI);const l=Math.floor(a/s+.5)%8,[c,h]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=c*c+h*h;let u=Math.max(0,Math.round((r*c+o*h)/(i*d)));if(Number.isFinite(n)&&n>=0){const t=(e,t)=>{if(!t)return 1/0;const r=t>0?n-e:e+n;return Math.max(0,Math.floor((r+1e-9*i)/i))};u=Math.min(u,t(e[0],c),t(e[1],h))}return[e[0]+c*u*i,e[1]+h*u*i]}function Et(e,t){if(t){const t=e/2.54;let i=Math.floor(t/12),n=Math.round(t-12*i);return 12===n&&(i+=1,n=0),`${i}′ ${n}″`}return`${(e/100).toFixed(2)} m`}function Nt(e,t,i=1){const n=e[0].toFixed(i),r=e[1].toFixed(i),o=t[0].toFixed(i),s=t[1].toFixed(i),a=n=3?e.poly:e&&null!=e.x&&null!=e.y&&null!=e.w&&null!=e.h?[[e.x,e.y],[e.x+e.w,e.y],[e.x+e.w,e.y+e.h],[e.x,e.y+e.h]]:null}function Lt(e){const t=[];for(const i of e||[])i?.poly?.length>=3?t.push({poly:i.poly.map(e=>e.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&t.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return t}function Bt(e){const t=[],i=new Set;for(const n of e||[]){const e=Ht(n);if(e)for(let n=0;n=90?e-=180:e<-90&&(e+=180),n.step&&n.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(n.length||0,0)/2,i/2);let a=Math.round(u*i/n.step)*n.step;Math.abs(u*i-i/2)<=n.step/2&&(a=i/2),a=Math.max(o,Math.min(i-o,a));const l=a/i;r={x:t+l*c,y:s+l*h,angle:e}}else r={x:p[0],y:p[1],angle:e}}}return r}function Wt(e,t,i){let n=null,r=1/0;for(let o=0;o=r)continue;r=m;const _=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(_,Math.round(p*_/i)*i)):p*_)/_;n=[s+g*h,a+g*d]}return n}function jt(e,t,i,n,r,o=1){const s=t*Math.PI/180,a=[Math.cos(s),Math.sin(s)];let l=null,c=o;for(const t of Bt(n)){const i=[[t[0],t[1]],[t[2],t[3]]],n=t=>Math.abs(a[0]*(t[1]-e[1])-a[1]*(t[0]-e[0]));if(n(i[0])>o||n(i[1])>o)continue;const r=(i[0][0]-e[0])*a[0]+(i[0][1]-e[1])*a[1],s=(i[1][0]-e[0])*a[0]+(i[1][1]-e[1])*a[1],h=Math.min(r,s),d=Math.max(r,s),u=h>0?h:d<0?-d:0;u[e[0]+a[0]*t,e[1]+a[1]*t],g=(h+d)/2;return{wallA:_(h),wallB:_(d),sideA:p,sideB:m,midA:_((h-u)/2),midB:_((u+d)/2),wallCenter:_(g),centered:Math.abs(g)<=r}}function Ut(e,t,i=!1,n){if("passage"===e)return 1;if(null==t||"unavailable"===t||"unknown"===t)return"window"===e?0:1;const r="string"==typeof n?n.trim()?Number(n):NaN:Number(n),o=null!=n&&Number.isFinite(r)?Math.max(0,Math.min(1,r/100)):function(e){return["on","open","home","detected","playing","cleaning"].includes(String(e))}(t)?1:0;return i?1-o:o}function Vt(e){const t=Number.isFinite(e)?Math.max(0,Math.min(1,e)):0;return Math.min(1,Math.max(0,.05*Math.round(t/.05)))}function Gt(e,t){return(Number.isFinite(e)&&e>0?e:0)*(Number.isFinite(t)?Math.max(0,Math.min(1,t)):0)}function Kt(e){return"passage"===e.type?[]:[e.contact,e.lock].filter(e=>"string"==typeof e&&e.length>0)}function Yt(e,t,i=200){const n=t.trim().toLowerCase();return(n?e.filter(e=>e.label.toLowerCase().includes(n)||e.value.toLowerCase().includes(n)):e).slice(0,Math.max(0,i))}function Xt(e,t,i=.001){return Math.abs(e[0]-t[0])e[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s)+o&&(i=!i)}return i}function Jt(e,t,i){const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;let s=o?((e[0]-t[0])*n+(e[1]-t[1])*r)/o:0;return s=Math.max(0,Math.min(1,s)),Math.hypot(e[0]-(t[0]+s*n),e[1]-(t[1]+s*r))}function Qt(e,t){if(!t||t.length<2)return null;let i=null,n=1/0;for(let r=0;rr&&s<-r||o<-r&&s>r)&&(a>r&&l<-r||a<-r&&l>r)}function ri(e,t=24){const i=e.map(e=>e[0]),n=e.map(e=>e[1]),r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1;let c=0,h=0,d=0;for(let t=0;t1e-9?[h/(3*c),d/(3*c)]:[(r+o)/2,(s+a)/2],p=(t,i)=>{const n=((t,i)=>{if(!Zt([t,i],e))return-1/0;let n=1/0;for(let r=0;r_&&(_=c,m=[n,l])}if(m){const[e,i]=m,n=(o-r)/t,l=(a-s)/t;for(let t=-4;t<=4;t++)for(let r=-4;r<=4;r++){const o=e+n*t/4,s=i+l*r/4,a=p(o,s);a>_&&(_=a,m=[o,s])}}return m||oi(e)||e[0]}function oi(e,t=1e-6){if(!e||e.length<3)return null;const i=e.length,n=[e.reduce((e,t)=>e+t[0],0)/i,e.reduce((e,t)=>e+t[1],0)/i];if(ti(n,e,t))return n;for(let n=0;nai(e,t,i));return n.filter(e=>!n.some(t=>t!==e&&ai(t,e,i)))}function hi(e){if(!e||e.length<3)return 0;let t=0;for(let i=0;i[e[0],e[1]]),[e[0][0],e[0][1]]]]}function ui(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=Mt(di(e),di(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const n=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return n.length>=3?n:null}function pi(e,t,i){for(let n=0;n1&&Xt(i[0],i[i.length-1],t)&&i.pop(),i}function _i(e,t,i=1e-6){if(!e||e.length<3||!t||t.length<2)return null;const n=t[0],r=t[t.length-1];if(Xt(n,r,i))return null;const o=pi(e,n,i),s=pi(e,r,i);if(o<0||s<0)return null;const a=t.slice(1,-1);for(const t of a)if(!ti(t,e,i))return null;for(let i=0;i{const s=[t];let a=(n+1)%e.length;for(let t=0;t<=e.length&&(s.push(e[a]),a!==o);t++)a=(a+1)%e.length;return s.push(r),mi(s,i)};let c,h;if(o===s){const s=mi([...t],i);if(s.length<3||hi(s)<=i)return null;const a=[];for(let i=0;i=0?t:[...t].reverse();for(const e of i)a.push(e)}c=mi(a,i),h=s}else c=mi([...l(n,o,r,s),...[...a].reverse()],i),h=mi([...l(r,s,n,o),...a],i);return c.length<3||h.length<3||hi(c)<=i||hi(h)<=i||Math.abs(hi(c)+hi(h)-hi(e))>Math.max(i,1e-6*hi(e))?null:[c,h]}function gi(e,t,i){const[n,r]=e.split(":");return"device"===n?r:"entity"===n?"lg_"+r:t&&t.startsWith("v_")?t:i()}function fi(e){return e.length?Math.round(e.reduce((e,t)=>e+t,0)/e.length):null}function vi(e,t){if(t>e[2]/e[3]){const i=e[3],n=e[3]*t;return{x:e[0]-(n-e[2])/2,y:e[1],w:n,h:i}}const i=e[2],n=e[2]/t;return{x:e[0],y:e[1]-(n-e[3])/2,w:i,h:n}}function yi(e,t,i,n){if(e.length<2)return;const r=t.x+n,o=t.x+t.w-n,s=t.y+n,a=t.y+t.h-n;for(let t=0;t<60;t++){let t=!1;for(let n=0;n=0)i=t.slice(0,r).trim(),n=t.slice(r+1).trim();else{const e=t.split(".");e.length>2&&(i=e.slice(0,2).join("."),n=e.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?r>=0&&!n||n&&!/^[a-zA-Z0-9_.-]+$/.test(n)?null:n?{entity:i,attr:n}:{entity:i}:null}function zi(e,t){const i=String(e??"").trim(),n=String(t??"").trim(),r=Di(n?`${i}:${n}`:i);return r?`{${r.entity}${r.attr?`:${r.attr}`:""}}`:""}function Ai(e,t,i){const n=String(t??"").trim();if(!n)return null;const r=e?.states?.[n];if(!r)return null;const o=String(i??"").trim(),s=e=>e.slice(0,60);if(o){const t=function(e){if(null==e)return null;if(Array.isArray(e)){const t=e.map(e=>null==e?"":String(e)).join(", ");return t?t.slice(0,60):null}if("object"==typeof e)return null;const t=String(e);return""===t?null:t.slice(0,60)}(r.attributes?.[o]);if(null===t)return null;const i=e?.formatEntityAttributeValue;if("function"==typeof i)try{const t=i.call(e,r,o);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:t,formatted:!1}}const a=r.state;if(null==a||""===a)return null;const l=e?.formatEntityState;if("function"==typeof l)try{const t=l.call(e,r);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:s(String(a)),formatted:!1}}function Pi(e,t,i){const n=String(t??"").trim(),r=String(i??"").trim()||n;if(!r)return e.text;const o=e.formatted&&n?function(e,t){if(!t)return e;const i=e.replace(/\s+$/,"");return i.endsWith(t)?i.slice(0,i.length-t.length).replace(/\s+$/,""):e}(e.text,n):e.text;return`${o} ${r}`}function Fi(e,t){const i=(t?.entity||"").trim();if(!i)return"";const n=e?.states?.[i],r=n?.state;if(!n||null==r||""===r||"unavailable"===r||"unknown"===r)return Ri;const o=(t?.attr||"").trim(),s=Ai(e,i,o||null);if(null===s)return Ri;return Pi(s,o?"":String(n.attributes?.unit_of_measurement??"").trim(),t?.unit)}function Oi(e,t,i,n=()=>!0){const r=e??"";let o=!1;const s=r.replace(/\{([^{}\r\n]+)\}/g,(e,t)=>{const r=Di(t);return r?(o=!0,n(r.entity||"")?Fi(i,r):Ri):e});if(o)return s;const a=(t?.entity||"").trim();if(!a)return r;if(!n(a)){const e=r.indexOf(Ti);return e>=0?r.slice(0,e)+Ri+r.slice(e+2):r?`${r} ${Ri}`:Ri}const l=Fi(i,t),c=r.indexOf(Ti);return c>=0?r.slice(0,c)+l+r.slice(c+2):r?`${r} ${l}`:l}const Ii=20;function Ei(e){const t=Number(e?.scale);if(Number.isFinite(t)&&t>0)return Math.min(20,Math.max(.15,t));return{s:.7,m:1,l:1.5}[String(e?.size??"")]??1}function Ni(e,t){if(!t)return e;let i=e;for(const[e,n]of Object.entries(t))i=i.split("{"+e+"}").join(String(n));return i}const Hi="#55606c",Li=.55,Bi=20,qi=25,Wi={c:"#607d8b",a:.18};function ji(e,t=Wi){const i=e&&"object"==typeof e?e:null,n=i?.a;return{c:Dt(i?.c,t.c),a:"number"==typeof n&&Number.isFinite(n)?Math.min(1,Math.max(0,n)):t.a}}function Ui(e,t){const i=ji(e),n=t?.settings?.custom_fill;return n&&"object"==typeof n?ji(n,i):i}function Vi(e){const t=e?.settings||{},i=!e?.plan_url,n="glow"===t.fill_mode;return{showBorders:t.show_borders??i,showNames:t.show_names??i,color:Dt(t.room_color,Hi),opacity:"number"==typeof t.room_opacity?Math.min(1,Math.max(0,t.room_opacity)):.55,fill:["lqi","light","temp","custom"].includes(t.fill_mode)?t.fill_mode:"none",customFill:ji(t.custom_fill),glow:"boolean"==typeof t.glow_enabled?t.glow_enabled:n,tempMin:"number"==typeof t.temp_min?t.temp_min:20,tempMax:"number"==typeof t.temp_max?t.temp_max:25,showLqi:"boolean"==typeof t.show_lqi?t.show_lqi:null,cardFontScale:"number"==typeof t.card_font_scale&&t.card_font_scale>0?Math.min(3,Math.max(.5,t.card_font_scale)):1,labelTemp:!0===t.label_temp,labelHum:!0===t.label_hum,labelLqi:!0===t.label_lqi,labelLight:!0===t.label_light,bgColor:Dt(t.bg_color,null),hideDecor:!0===t.hide_decor,hideOpenings:!0===t.hide_openings}}function Gi(e,t){if(t.bgColor)return t.bgColor;const i=e?.bg_color;return Dt(i,"")}function Ki(e){return!1!==e?.show_room_tooltip}const Yi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}};function Xi(e){const t={},i=e?.fill_colors||{};for(const e of Object.keys(Yi)){const n=Yi[e],r=i[e];t[e]={c:Dt(r?.c,n.c),a:r&&"number"==typeof r.a?Math.min(1,Math.max(0,r.a)):n.a}}return t}function Zi(e,t,i){const n=Math.min(1,Math.max(0,i)),r=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),s=r.map((e,t)=>Math.round(e+(o[t]-e)*n));return"#"+s.map(e=>e.toString(16).padStart(2,"0")).join("")}function Ji(e,t,i,n,r,o,s,a=Wi){const l=function(e,t,i,n,r,o,s,a=Wi){if("custom"===e)return ji(a);if("lqi"===e){if(null==t)return null;const e=(t-40)/140;return{c:Zi(s.lqi_low.c,s.lqi_high.c,e),a:s.lqi_low.a+(s.lqi_high.a-s.lqi_low.a)*Math.min(1,Math.max(0,e))}}if("light"===e)return"none"===i?s.light_none.a>0?s.light_none:null:"on"===i?s.light_on:s.light_off;if("temp"===e){if(null==n)return null;const e=Math.min(r,o),t=Math.max(r,o);return nt?s.temp_hot:s.temp_ok}return null}(e,t,i,n,r,o,s,a);return l?{color:l.c,opacity:l.a,mode:e}:null}const Qi={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},en=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function tn(e){for(const t of[...Object.values(Qi),...en])if(e===t[0]||e===t[1])return t;return null}function nn(e,t,i,n,r){if(!n||"unavailable"===n||"unknown"===n)return e;if(r){const i="cover"===t?tn(e):null;return i?"closed"===n?i[0]:i[1]:e}if("binary_sensor"===t){if("door"===i)return"on"===n?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===n?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===n?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===t){const t=Qi[String(i||"")];if(t)return"closed"===n?t[0]:t[1];const r=tn(e);return r?"closed"===n?r[0]:r[1]:e}return"lock"===t?"locked"===n?"mdi:lock":"mdi:lock-open-variant":"light"===t&&"mdi:lightbulb"===e&&"on"===n?"mdi:lightbulb-on":e}function rn(e){const t=Math.min(4e4,Math.max(1e3,e))/100,i=t<=66?255:329.698727446*Math.pow(t-60,-.1332047592),n=t<=66?99.4708025861*Math.log(t)-161.1195681661:288.1221695283*Math.pow(t-60,-.0755148492),r=t>=66?255:t<=19?0:138.5177312231*Math.log(t-10)-305.0447927307,o=e=>Math.round(Math.min(255,Math.max(0,e)));return[o(i),o(n),o(r)]}const on=1/2.2;function sn(e){const t=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(e);return t?"#"+t.slice(1).map(e=>Math.min(255,Number(e)).toString(16).padStart(2,"0")).join(""):null}function an(e){if(!e||"object"!=typeof e||Array.isArray(e))return null;const t=e;if(Object.keys(t).some(e=>"c"!==e&&"bri"!==e))return null;const i=Dt(t.c,null);return i?void 0===t.bri||null===t.bri?{c:i}:"number"!=typeof t.bri||!Number.isFinite(t.bri)||t.bri<.01||t.bri>1?null:{c:i,bri:t.bri}:null}function ln(e,t,i){const n=an(t),r=e?.attributes||{},o=n?.bri??function(e){const t=e?.attributes?.brightness,i="number"==typeof t?t:"string"==typeof t&&""!==t.trim()?Number(t):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(e);if(n)return{c:n.c,bri:o};const s=Pt(r.rgb_color);if(s)return{c:sn(s)||Dt(i,"#ffd9a0"),bri:o};const a=Number(r.color_temp_kelvin)||(Number(r.color_temp)>0?1e6/Number(r.color_temp):NaN);return Number.isFinite(a)&&a>0?{c:(l=rn(a),"#"+l.slice(0,3).map(e=>Math.min(255,Math.max(0,Math.round(Number(e)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Dt(i,"#ffd9a0"),bri:o};var l}function cn(e,t,i){return"on"===e?.state?ln(e,t,i):null}function hn(e,t=1){const i=Math.max(0,Math.min(1,Number.isFinite(e)?e:1)),n=.7*Math.max(0,Math.min(1,Number.isFinite(t)?t:1))*(.4+.6*Math.pow(i,on));return Math.max(0,Math.min(1,n))}function dn(e){return e.startsWith("light.")||e.startsWith("switch.")}function un(e,t,i=1e-6){const n=[];if(!e||!t||e.length<3||t.length<3)return n;for(let r=0;ru||l>u)continue;const p=(r[0]-o[0])*h+(r[1]-o[1])*d,m=(s[0]-o[0])*h+(s[1]-o[1])*d,_=Math.max(0,Math.min(p,m)),g=Math.min(c,Math.max(p,m));g-_>i&&n.push([o[0]+h*_,o[1]+d*_,o[0]+h*g,o[1]+d*g])}}return n}function pn(e,t,i=1e-6){const n=[];for(const r of e){const e=[r[0],r[1]],o=[r[2],r[3]],s=o[0]-e[0],a=o[1]-e[1],l=Math.hypot(s,a);if(lo||r>o)continue;const s=(n[0]-e[0])*c+(n[1]-e[1])*h,a=(n[2]-e[0])*c+(n[3]-e[1])*h,u=Math.max(0,Math.min(s,a)),p=Math.min(l,Math.max(s,a));p-u>i&&d.push([u,p])}if(!d.length){n.push([e[0],e[1],o[0],o[1]]);continue}d.sort((e,t)=>e[0]-t[0]);let u=0;for(const[t,r]of d)t-u>i&&n.push([e[0]+c*u,e[1]+h*u,e[0]+c*t,e[1]+h*t]),u=Math.max(u,r);l-u>i&&n.push([e[0]+c*u,e[1]+h*u,o[0],o[1]])}return n}function mn(e,t,i=1e-6){const n=[];for(let t=0;t{if("string"!=typeof e||!e)return;const i=vn(e);i.startsWith("/api/houseplan/content/")&&t.add(i)};for(const t of e?.spaces||[]){i(t?.plan_url);for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url)}for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url);return t}function vn(e){return e?e.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+e.slice(23):e.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+e.slice(23):e:""}function yn(e,t){const i=t?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:e}function bn(e,t){const i=t?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||e}function wn(e,t,i,n){if(!t||!i||t===i)return e;const r="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(r))return e;const t=e.url.split(r)[1]||"",[i,s]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(n){const t=n[decodeURIComponent(i)]??n[i];return t?{...e,url:e.url.split(r+i)[0]+o+encodeURIComponent(t)+s}:e}return{...e,url:e.url.split(r).join(o)}})}function kn(e,t=1){const i=Number(e);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):t}function xn(e,t,i){let n=null,r=null;for(const o of t){if(!(Math.abs(o[0]-e[0])<1e-6&&Math.abs(o[1]-e[1])<1e-6)){if(Math.abs(o[0]-e[0])<=i){const t=Math.abs(o[1]-e[1]);t>1e-6&&(!n||t1e-6&&(!r||t"number"==typeof e&&Number.isFinite(e),In=(e,t,i)=>Math.min(i,Math.max(t,e));function En(e){return e.elevation<=-6?"night":e.elevation>=6?"day":e.rising?"dawn":"dusk"}function Nn(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;return t>=300&&t<480?"dawn":t>=480&&t<1080?"day":t>=1080&&t<1260?"dusk":"night"}function Hn(e){const t=En(e),i=e.azimuth*Math.PI/180;return{sunX:50-42*Math.sin(i),sunY:78-In(e.elevation,0,90)/90*64,sunOpacity:"night"===t?0:In((e.elevation+6)/12,0,1)}}function Ln(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;if(t<300||t>=1260)return{sunX:50,sunY:78,sunOpacity:0};const i=(t-300)/960,n=(t-300)/120,r=(1260-t)/120;return{sunX:8+84*i,sunY:78-64*Math.sin(i*Math.PI),sunOpacity:Math.max(.18,Math.min(n,r,1))}}function Bn(e,t=new Date){const i=function(e){const t=e?.states?.["sun.sun"]?.attributes;return t&&On(t.azimuth)&&On(t.elevation)&&"boolean"==typeof t.rising?{azimuth:An(t.azimuth),elevation:t.elevation,rising:t.rising}:null}(e);if(i)return{phase:En(i),source:"sun",...Hn(i)};const n="number"==typeof t?t:function(e=new Date){return 60*e.getHours()+e.getMinutes()}(t);return{phase:Nn(n),source:"clock",...Ln(n)}}function qn(e){return`${e.source}|${e.phase}|${e.sunX.toFixed(2)}|${e.sunY.toFixed(2)}|`+e.sunOpacity.toFixed(3)}const Wn=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],jn=e=>Math.min(1,Math.max(0,e));function Un(e,t,i=6){const n=e.angle*Math.PI/180,r=[Math.sin(n),-Math.cos(n)],o=n=>{const o=[e.x+r[0]*i*n,e.y+r[1]*i*n];return t.find(e=>e.poly.length>=3&&Zt(o,e.poly))||null},s=o(1),a=o(-1);return s&&a?null:s||a?s?{normal:[-r[0],-r[1]],roomId:s.id}:{normal:r,roomId:a.id}:null}function Vn(e,t,i){return i>0&&e[0]*t[0]+e[1]*t[1]>.05}function Gn(e,t,i,n){return[[e[0],e[1]],[t[0],t[1]],[t[0]+i[0]*n,t[1]+i[1]*n],[e[0]+i[0]*n,e[1]+i[1]*n]]}function Kn(e,t){try{const i=Ct([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]),n=[];for(const e of i){const t=e?.[0];!Array.isArray(t)||t.length<4||n.push(t.slice(0,t.length-1).map(e=>[e[0],e[1]]))}return n}catch{return[]}}function Yn(e,t,i,n,r,o,s){if(!(n>0))return[];const a=Pn(i,r),l=[-a[0],-a[1]],c=function(e){const t=Math.min(90,Math.max(0,e));return.7*(.8+1.7*Math.pow(1-t/90,1.6))}(n),h=[];for(const i of t){if(!(i.length>0))continue;const t=Un(i,e);if(!t||!Vn(t.normal,a,n))continue;const r=e.find(e=>e.id===t.roomId);if(!r)continue;const d=o&&o[t.roomId]||r.poly,u=i.angle*Math.PI/180,p=i.length/2,m=[-t.normal[0],-t.normal[1]],_=Math.max(0,s?.[i.id]||0),g=i.x+m[0]*_/2,f=i.y+m[1]*_/2,v=Math.cos(u)*p,y=Math.sin(u)*p,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,x=Kn(Gn(b,w,l,k),d);if(!x.length)continue;const $=l[0]*m[0]+l[1]*m[1];h.push({openingId:i.id,roomId:t.roomId,polys:x,a:b,b:w,dir:l,len:k,normal:m,depth:k*$})}return h}const Xn=e=>"number"==typeof e&&Number.isInteger(e)&&e>=0&&e<=359?e:null;function Zn(e,t){const i=Xn(t?.north_deg);return null!==i?i:Xn(e?.north_deg)}function Jn(e,t){const i=e=>"static"===e||"daynight"===e?e:null;return i(t?.bg_mode)??i(e?.bg_mode)??"static"}function Qn(e,t){const i=t?.sun_rays;return"boolean"==typeof i?i:!0===e?.sun_rays}function er(e){const t=e?.states?.["sun.sun"]?.attributes,i=Number(t?.azimuth),n=Number(t?.elevation);return Number.isFinite(i)&&Number.isFinite(n)?{azimuth:i,elevation:n}:null}const tr=["dawn","day","dusk","night"];function ir(e){if(!e)return"";const t=Fn[e.phase];return[`--hp-day-cycle-outline-near:${t.outlineNear}`,`--hp-day-cycle-outline-mid:${t.outlineMid}`,`--hp-day-cycle-outline-far:${t.outlineFar}`].join(";")}function nr(e,t=1){if(!e)return V;const i=[`--hp-day-cycle-sun-x:${e.sunX.toFixed(2)}%`,`--hp-day-cycle-sun-y:${e.sunY.toFixed(2)}%`,`--hp-day-cycle-sun-opacity:${e.sunOpacity.toFixed(3)}`,`opacity:${Math.min(1,Math.max(0,t)).toFixed(4)}`].join(";");return W``}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}render(){if(!this._config||!this.hass)return V;const e=uu(this,fu,xu(this.hass,this._config.language));if("cold"===e)return pu();if("warm"===e)return U;const t=this._snap?.config;if(!t)return this._errorCard(Su(this._lang,"space_card.loading"));const i=this._config.space,n=Da(t).find(e=>e.id===i),r=void 0!==this._config.title?this._config.title:n?.title||"",o=this._renderDeviceSnapshot,s=Qm({hass:o?.hass||this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:i,iconSize:this._config.icon_size,fit:this._config.fit,compactTopFrame:""===this._config.title||null===this._config.title,stageWidth:this._stageWidth,lang:this._lang,displayUrl:e=>this._signer.display(this.hass,e),decorAssetUrl:e=>this._decorAssets.get(e)?.url||"",assetLoaded:this._onAssetLoaded,registry:o?{revision:o.sourceSequence,authoritative:!1,access:"limited",entities:{...o.hass.entities},devices:{...o.hass.devices},lastSuccess:o.capturedAt}:mh(this.hass),devices:[...o?.devices||this._devices],areaRelocationIds:this._areaRelocationIds,presentations:o?.presentations,activityRuntime:this._activityRuntime,reducedMotion:this._reducedMotion,virtualLights:this._snap?.virtualLights,lightPools:!0===this._config.light_pools,glowRuntime:{state:this._glowRuntimeState,host:this._glowRuntimeHost,screenBlend:this._glowScreenBlend},liveStates:!1!==this._config.live_states,showTemperature:!1!==this._config.show_temperature,showSignal:!1!==this._config.show_signal,inert:this._continuity.overlayBlocksInteraction});if(!s)return this._errorCard(Su(this._lang,"space_card.not_found",{id:i}));const a=!1!==this._config.show_button,l=this._config.button_label||Su(this._lang,"space_card.button"),c=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return W` ${l}
`:V} - `}}w_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},w_.styles=[su,o` + `}}x_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},x_.styles=[lu,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -4765,7 +4765,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",w_),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-space-card"===e.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only live schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const k_=o` + `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",x_),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-space-card"===e.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only live schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const S_=o` :host { --hp-editor-tray-bg-fallback: color-mix(in srgb, var(--card-background-color, var(--hp-bg)) 94%, var(--hp-line)); --hp-editor-tray-bg: color-mix(in srgb, var(--card-background-color, var(--hp-bg)) 86%, transparent); @@ -4936,7 +4936,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef .editor-secondary { transition: none; } .editor-group-launcher .group-chevron { transition: none; } } -`;function x_(e,t){return e.catch(()=>{}).then(t)}function $_(e,t,i,n,r){return{previous:JSON.parse(JSON.stringify(e)),previousFingerprint:i,revision:n,attempted:t,attemptedFingerprint:r(t)}}function S_(e,t,i){const n=e._serverCfg;return!(!n||e._cfgRev!==t.revision||i(n)!==t.attemptedFingerprint)&&(e._serverCfg=t.previous,e._cfgContentFingerprint=t.previousFingerprint,e.requestUpdate(),!0)}class M_{constructor(e=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(e))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(e){this._undo.push(e),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const e=this._undo.pop()??null;return e&&this._redo.push(e),e}redo(){const e=this._redo.pop()??null;return e&&this._undo.push(e),e}removeWhere(e){const t=this._undo.length,i=this._redo.length;return this._undo=this._undo.filter(t=>!e(t)),this._redo=this._redo.filter(t=>!e(t)),t+i-this._undo.length-this._redo.length}clear(){this._undo=[],this._redo=[]}}function C_(e,t){const i=e[t];return i&&Number.isFinite(i.x)&&Number.isFinite(i.y)?{x:i.x,y:i.y,..."string"==typeof i.s?{s:i.s}:{}}:null}function R_(e,t,i){if(null===i){if(!(t in e))return e;const i={...e};return delete i[t],i}const n={...e[t]||{}};delete n.x,delete n.y,delete n.s;const r={...n,x:i.x,y:i.y,...void 0!==i.s?{s:i.s}:{}};return{...e,[t]:r}}const T_=Object.freeze({enabled:!1,z2mBaseTopics:Object.freeze([])});function D_(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^\/+|\/+$/g,"").replace(/\/{2,}/g,"/");return!t||t.length>180||/[#+\u0000-\u001f\u007f]/.test(t)?null:t}function z_(e){const t=e?.zigbee_topology;if(!t||"object"!=typeof t)return T_;const i=t,n=Array.isArray(i.z2m_base_topics)?i.z2m_base_topics:Array.isArray(i.z2mBaseTopics)?i.z2mBaseTopics:[],r=new Set,o=[];for(const e of n){const t=D_(e);if(t&&!r.has(t)&&(r.add(t),o.push(t),8===o.length))break}return{enabled:!0===i.enabled,z2mBaseTopics:o}}function A_(e,t){const i={...e},n=t.z2mBaseTopics.map(D_).filter(Boolean);return t.enabled||n.length?i.zigbee_topology={enabled:!0===t.enabled,z2m_base_topics:[...new Set(n)].slice(0,8)}:delete i.zigbee_topology,i}const P_={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},F_=e=>Math.max(0,Math.min(1,e)),O_=(e,t,i)=>e+(t-e)*i,I_=(e,t,i)=>{const n=1-e;return 3*n*n*e*t+3*n*e*e*i+e*e*e},E_=e=>{const t=F_(e);if(0===t||1===t)return t;let i=0,n=1,r=t;for(let e=0;e<12;e++)r=(i+n)/2,I_(r,.2,.2)0&&e.w>0?t/e.w:1,viewBox:{...e}}}function H_(e,t,i){const n=Math.max(1e-9,e.pixelsPerUnit),r=Math.max(1e-9,t/n),o=Math.max(1e-9,i/n);return{x:e.centerX-r/2,y:e.centerY-o/2,w:r,h:o}}function L_(e){const t=e.trim(),i=/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/i.exec(t);if(i)return[Number(i[1]),Number(i[2]),Number(i[3]),null==i[4]?1:Number(i[4])];const n=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return n?[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16),8===n.length?parseInt(n.slice(6,8),16)/255:1]:null}function B_(e,t,i){const n=L_(e),r=L_(t);if(!n||!r)return i<.5?e:t;const o=F_(i);return`rgba(${Math.round(O_(n[0],r[0],o))}, ${Math.round(O_(n[1],r[1],o))}, ${Math.round(O_(n[2],r[2],o))}, ${O_(n[3],r[3],o).toFixed(3)})`}class q_{constructor(e,t=P_){this._hooks=e,this._clock=t,this._token=0,this._raf=0,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase||"preparing"===this._state?.phase}get presented(){return this._state?.presented||null}start(e,t,i,n=220){this.cancel(!1);const r=++this._token,o=this._clock.now();if(this._state={token:r,phase:n<=0?"settling":"running",from:e,to:t,presented:n<=0?t:e,startedAt:o,duration:Math.max(0,n),targetMode:i},n<=0)return this._hooks.frame(this._state),this._hooks.settled(this._state),this._state=null,r;this._hooks.frame(this._state);const s=e=>{const t=this._state;if(!t||t.token!==r)return;const i=F_((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:function(e,t,i){const n=E_(i),r=O_(e.stageWidth,t.stageWidth,n),o=O_(e.stageHeight,t.stageHeight,n),s=Math.max(1e-9,e.viewport.pixelsPerUnit),a=Math.max(1e-9,t.viewport.pixelsPerUnit),l={centerX:O_(e.viewport.centerX,t.viewport.centerX,n),centerY:O_(e.viewport.centerY,t.viewport.centerY,n),pixelsPerUnit:Math.exp(O_(Math.log(s),Math.log(a),n))};return{presentedMode:n<.5?e.presentedMode:t.presentedMode,editorChromeHeight:O_(e.editorChromeHeight,t.editorChromeHeight,n),stageWidth:r,stageHeight:o,viewport:{...l,viewBox:H_(l,r,o)},stageColor:B_(e.stageColor,t.stageColor,n),paperColor:B_(e.paperColor,t.paperColor,n),sceneBrightness:O_(e.sceneBrightness,t.sceneBrightness,n),architectureOpacity:O_(e.architectureOpacity,t.architectureOpacity,n),backdropOpacity:O_(e.backdropOpacity,t.backdropOpacity,n),viewWeight:O_(e.viewWeight,t.viewWeight,n),editorWeight:O_(e.editorWeight,t.editorWeight,n),toolbarContentOpacity:O_(e.toolbarContentOpacity,t.toolbarContentOpacity,n)}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(s):(this._raf=0,this._hooks.settled(t),this._state?.token===r&&(this._state=null))};return this._raf=this._clock.requestFrame(s),r}cancel(e=!0){this._raf&&this._clock.cancelFrame(this._raf),this._raf=0;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=t.to,this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}}const W_={now:()=>"undefined"!=typeof performance?performance.now():Date.now(),requestFrame:e=>"function"==typeof requestAnimationFrame?requestAnimationFrame(e):null,cancelFrame:e=>{"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(e)}},j_=e=>Number.isFinite(e);function U_(e){return!!e&&j_(e.zoom)&&e.zoom>0&&j_(e.viewBox.x)&&j_(e.viewBox.y)&&j_(e.viewBox.w)&&e.viewBox.w>0&&j_(e.viewBox.h)&&e.viewBox.h>0}function V_(e){return{zoom:e.zoom,viewBox:{...e.viewBox}}}function G_(e,t){return Math.abs(e.zoom-t.zoom)<1e-9&&Math.abs(e.viewBox.x-t.viewBox.x)<1e-6&&Math.abs(e.viewBox.y-t.viewBox.y)<1e-6&&Math.abs(e.viewBox.w-t.viewBox.w)<1e-6&&Math.abs(e.viewBox.h-t.viewBox.h)<1e-6}const K_=(e,t,i)=>e+(t-e)*i,Y_=(e,t,i)=>Math.exp(K_(Math.log(e),Math.log(t),i));class X_{constructor(e,t=W_){this._hooks=e,this._clock=t,this._token=0,this._raf=null,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase}get presented(){return this._state?.presented||null}get target(){return this._state?.to||null}start(e,t,i,n){this.cancel(!1);const r=++this._token,o=V_(e),s=V_(t),a=n>0&&U_(o)&&U_(s),l=this._clock.now(),c={token:r,phase:a?"running":"settling",from:o,to:s,presented:V_(a?o:s),startedAt:l,duration:a?n:0,reason:i};if(this._state=c,this._hooks.frame(c),!a)return this._settle(r),r;const h=e=>{const t=this._state;if(!t||t.token!==r)return;const i=Math.max(0,Math.min(1,(e-t.startedAt)/t.duration));if(t.presented=i>=1?V_(t.to):function(e,t,i){if(!U_(e))return V_(t);if(!U_(t))return V_(e);const n=E_(i);if(n<=0)return V_(e);if(n>=1)return V_(t);const r=e.viewBox.x+e.viewBox.w/2,o=e.viewBox.y+e.viewBox.h/2,s=t.viewBox.x+t.viewBox.w/2,a=t.viewBox.y+t.viewBox.h/2,l=Y_(e.viewBox.w,t.viewBox.w,n),c=Y_(e.viewBox.h,t.viewBox.h,n),h=K_(r,s,n),d=K_(o,a,n);return{zoom:Y_(e.zoom,t.zoom,n),viewBox:{x:h-l/2,y:d-c/2,w:l,h:c}}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i>=1)return this._raf=null,void this._settle(r);this._raf=this._clock.requestFrame(h),null===this._raf&&this._settle(r)};return this._raf=this._clock.requestFrame(h),null===this._raf&&(c.phase="settling",c.presented=V_(s),this._hooks.frame(c),this._settle(r)),r}cancel(e=!0){null!==this._raf&&this._clock.cancelFrame(this._raf),this._raf=null;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=V_(t.to),this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}_settle(e){const t=this._state;t&&t.token===e&&(t.phase="settling",t.presented=V_(t.to),this._hooks.settled(t),this._state?.token===e&&(this._state=null))}}const Z_=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])}),J_=64;function Q_(e){return[e.rotDeg,e.tiltDeg,e.xyScale,e.zScale,e.origin[0],e.origin[1]].every(Number.isFinite)&&Math.abs(e.xyScale)>1e-12&&Math.abs(Math.cos(e.tiltDeg*Math.PI/180))>1e-12}function eg(e,t,i=Z_){if(!(Q_(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const n=i.rotDeg*Math.PI/180,r=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],s=e[1]-i.origin[1],a=(o*Math.cos(n)-s*Math.sin(n))*i.xyScale,l=(o*Math.sin(n)+s*Math.cos(n))*i.xyScale;return[i.origin[0]+a,i.origin[1]+l*Math.cos(r)-t*i.zScale*Math.sin(r)]}function tg(e,t=Z_){if(!Q_(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,n=t.tiltDeg*Math.PI/180,r=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(n));return[t.origin[0]+r*Math.cos(i)+o*Math.sin(i),t.origin[1]-r*Math.sin(i)+o*Math.cos(i)]}function ig(e=Z_){return`matrix(${function(e=Z_){if(!Q_(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,n=e.xyScale*Math.cos(t),r=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),s=e.xyScale*Math.cos(t)*Math.cos(i);return[n,o,r,s,e.origin[0]-n*e.origin[0]-r*e.origin[1],e.origin[1]-o*e.origin[0]-s*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function ng(e,t=Z_){const{rect:i,wallHeight:n}=e,r=e.openingHeight??n,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(o)||n<0||r<0||o<0)throw new Error("invalid isometric frame");const s=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],a=Math.max(n,r),l=s.flatMap(e=>[eg(e,-o,t),eg(e,a,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),u=Math.min(...h);return{x:d,y:u,w:Math.max(...c)-d,h:Math.max(...h)-u}}const rg=[".dev",".vacpuck",".oplock",".op-hit",".opening",".rlgo","a","button","input","select","textarea",'[contenteditable]:not([contenteditable="false"])',"[data-room-fit-block]",'[role="link"]','[role="button"]'].join(","),og=(e,t)=>{try{return"function"==typeof e?.matches&&e.matches(t)}catch{return!1}};function sg(e){for(const t of e){const e=t;if(og(e,".roomlabel[data-id]")){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(og(e,rg))return{kind:"interactive"};if(og(e,'[data-hp="room"][data-id]')){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(og(e,".stage"))return{kind:"background"}}return{kind:"outside"}}function ag(e){const t=sg(e);return"room"===t.kind?t.roomId:null}class lg{constructor(){this.pointer=null,this.sequence=null}clear(){this.pointer=null,this.sequence=null}clearOutside(e){let t={kind:"outside"};try{t=sg(e.composedPath())}catch{}"outside"===t.kind&&this.clear()}pointerDown(e,t,i){let n={kind:"outside"};try{n=sg(e.composedPath())}catch{}this.pointer=function(e){const t="mouse"===(i=e.pointerType)||"touch"===i||"pen"===i?i:null;var i;return t&&"view"===e.mode&&"background"===e.owner.kind&&!e.blocked&&e.isPrimary&&0===e.button&&Number.isFinite(e.x)&&Number.isFinite(e.y)?{pointerId:e.pointerId,spaceId:e.spaceId,modality:t,x:e.x,y:e.y}:null}({pointerId:e.pointerId,pointerType:e.pointerType,isPrimary:e.isPrimary,button:e.button,spaceId:t,mode:i?"view":"blocked",owner:n,blocked:!i,x:e.clientX,y:e.clientY}),this.pointer||(this.sequence=null)}pointerUp(e,t,i,n,r=Date.now()){let o={kind:"outside"};try{o=sg(e.composedPath())}catch{}const s=function(e,t,i){if(!t||"view"!==i.mode||"background"!==i.owner.kind||i.blocked||t.pointerId!==i.pointerId||t.spaceId!==i.spaceId||!Number.isFinite(i.x)||!Number.isFinite(i.y)||!Number.isFinite(i.now)||Math.abs(i.x-t.x)+Math.abs(i.y-t.y)>=8)return{sequence:null,trigger:!1};const n=e?i.now-e.at:Number.POSITIVE_INFINITY;return e&&e.spaceId===i.spaceId&&e.modality===t.modality&&n>=0&&n<=350?{sequence:null,trigger:!0}:{sequence:{at:i.now,spaceId:i.spaceId,modality:t.modality},trigger:!1}}(this.sequence,this.pointer,{pointerId:e.pointerId,spaceId:t,mode:i?"view":"blocked",owner:o,blocked:n||!i,x:e.clientX,y:e.clientY,now:r});return this.pointer=null,this.sequence=s.sequence,s.trigger}}const cg=e=>e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function hg(e){return!!e&&Number.isFinite(e.x)&&Number.isFinite(e.y)&&Number.isFinite(e.w)&&e.w>0&&Number.isFinite(e.h)&&e.h>0}function dg(e,t=0){if(!e.length||!Number.isFinite(t)||t<0||!e.every(cg))return null;let i=1/0,n=1/0,r=-1/0,o=-1/0;for(const t of e)i=Math.min(i,t[0]),n=Math.min(n,t[1]),r=Math.max(r,t[0]),o=Math.max(o,t[1]);const s=r-i,a=o-n;return s>0&&a>0?{x:i-t,y:n-t,w:s+2*t,h:a+2*t}:null}function ug(e,t){return`${e("editor.load_failed")} ${e(t.terminal?"editor.refresh_advice":"editor.retry_advice")}`}class pg extends Error{}class mg{constructor(e){this.options=e,this._state="idle",this._inFlight=null}get state(){return this._state}ensure(){return"ready"===this._state?Promise.resolve(!0):"failed"===this._state?Promise.resolve(!1):(this._inFlight||(this._setState("loading"),this._inFlight=this._loadWithRetry().finally(()=>{this._inFlight=null})),this._inFlight)}async _loadWithRetry(){let e=new Error("Editor runtime did not load"),t=!1;for(const i of[0,1])try{const e=await this.options.load(i);if(e.fingerprint!==this.options.expectedFingerprint)throw new pg(`Editor runtime fingerprint mismatch: expected ${this.options.expectedFingerprint}, got ${e.fingerprint}`);const t=e.create();return this.options.install(t),this._setState("ready"),!0}catch(i){e=i,i instanceof pg&&(t=!0)}return this._setState(t?"failed":"idle"),this.options.failed?.(e,{terminal:t}),!1}_setState(e){e!==this._state&&(this._state=e,this.options.stateChanged?.(e))}}const _g=(e,t)=>{if(!e.has(t))return{hit:!1};const i=e.get(t);return e.delete(t),e.set(t,i),{hit:!0,value:i}},gg=(e,t,i,n)=>{for(e.delete(t),e.set(t,i);e.size>n;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},fg=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),vg=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`,yg="houseplan_card_alpha_v1",bg=Object.freeze([Object.freeze({id:"iso",issue:89,summary:"Volumetric plan renderer"})]);function wg(e){return/^[a-z][a-z0-9-]*$/.test(e.id)&&Number.isInteger(e.issue)&&e.issue>0&&!!e.summary.trim()}function kg(e){const t=String(e||"").replace(/^#/,"");return new URLSearchParams(t)}function xg(e){return kg(e).get("space")||""}function $g(e,t,i=bg){const n=function(e){const t=new Set;for(const i of e){if(!wg(i)||t.has(i.id))return!1;t.add(i.id)}return!0}(i),r=function(e){let t,i=!1;const n=e=>{for(const n of e.getAll("hp_alpha"))i=!0,"1"===n?t=!0:"0"===n&&(t=!1)};return n(new URLSearchParams(String(e.search||"").replace(/^\?/,""))),n(kg(e.hash)),{present:i,value:t}}(e),o=void 0!==r.value,s="1"===t,a=o?!0===r.value:!r.present&&s,l=n&&a;return{alpha:l,active:Object.freeze(l?i.map(e=>e.id).sort():[]),space:xg(e.hash),persist:n&&o?l?"1":"0":void 0,knownUrlOperation:o}}let Sg={alpha:!1,active:Object.freeze([]),space:""},Mg=!1,Cg="";const Rg=new Set;function Tg(){if("undefined"==typeof window)return Sg;const e=$g(window.location,function(){try{return window.localStorage.getItem(yg)}catch{return null}}());void 0!==e.persist&&function(e){try{window.localStorage.setItem(yg,e)}catch{}}(e.persist),Sg={alpha:e.alpha,active:e.active,space:e.space},window.__hpAlpha=e.alpha,window.__hpLabs=e.active;for(const e of Rg)e(Sg);return Sg}function Dg(){Tg()}function zg(){if("undefined"==typeof window)return Sg;if(!Mg){Mg=!0,window.__hpLabsListenerCleanup?.(),window.addEventListener("hashchange",Dg),window.addEventListener("popstate",Dg);const e=()=>{window.removeEventListener("hashchange",Dg),window.removeEventListener("popstate",Dg),window.__hpLabsListenerCleanup===e&&delete window.__hpLabsListenerCleanup};window.__hpLabsListenerCleanup=e}return Tg()}const Ag=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Pg(e){let t=0;for(let i=0;iArray.isArray(e)&&Number.isFinite(e[0])&&Number.isFinite(e[1])).map(e=>[Number(e[0]),Number(e[1])]),i=t[t.length-1];return t.length>1&&t[0][0]===i[0]&&t[0][1]===i[1]&&t.pop(),t}(e);if(i.length<3)return[];const n=!t;return Pg(i)>0===n?i:[...i].reverse()}function Og(e,t,i){return e.length?`M ${e.map(e=>Ag(eg(e,i,t))).join(" L ")} Z`:""}function Ig(e,t,i){const n=t[1]-e[1],r=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return n*Math.sin(o)+r*Math.cos(o)}function Eg(e,t,i=Z_){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const n=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=Fg(e,t);if(!i.length)return i;let n=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(Pg(e))>=1e-9)).filter(e=>e.length&&Pg(e[0])>0);n.sort((e,t)=>{const i=e[0][0],n=t[0][0];return i[0]-n[0]||i[1]-n[1]||Math.abs(Pg(t[0]))-Math.abs(Pg(e[0]))});const r=[],o=[];let s=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:r.join(" "),sides:o,componentCount:n.length,edgeCount:s}}function Ng(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const Hg=e=>e.every(Number.isFinite);function Lg(e,t,i,n){const r=e[0]*i,o=e[1]*n,s=t*Math.PI/180;return[r*Math.cos(s)-o*Math.sin(s),r*Math.sin(s)+o*Math.cos(s)]}function Bg(e,t,i,n,r,o,s){const a=e.flipH?-1:1,l="gate"===e.type?1:e.flipV?-1:1,c=(e.type,e.flipV,e.angle,e.face,{ox:0,oy:0}),h=function(e,t){return[e[0]+t[0],e[1]+t[1]]}([e.x+c.ox,e.y+c.oy],Lg(i,e.angle,a,l));return{leaf:t,hinge:h,closedVector:Lg(n,e.angle,a,l),quarterVector:Lg([-n[1],n[0]],e.angle,a,l),turnDeg:r,bottom:o,top:s}}const qg=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Wg(e,t,i=Z_){const n=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const r=function(e,t){const i=e.turnDeg*t*Math.PI/180;return[e.hinge[0]+e.closedVector[0]*Math.cos(i)+e.quarterVector[0]*Math.sin(i),e.hinge[1]+e.closedVector[1]*Math.cos(i)+e.quarterVector[1]*Math.sin(i)]}(t,n),o=eg(t.hinge,t.bottom,i),s=eg(r,t.bottom,i),a=eg(r,t.top,i),l=eg(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${qg(o)} L ${qg(s)} L ${qg(a)} L ${qg(l)} Z`,shadowD:`M ${qg(eg(t.hinge,0,i))} L ${qg(eg(r,0,i))}`,depth:Math.max(o[1],s[1])}})}const jg={window:120,door:90,passage:90,gate:300};function Ug(e){return jg[e]}function Vg(e,t){return{type:e,lengthCm:Ug(e),flipH:!1,flipV:!1,revision:t}}function Gg(e,t){const i=Math.max(0,e.renderedLength)/2,n=Math.max(0,e.target.physicalHalfWidth),r=n+.18*Math.max(0,t),o=e=>({x1:e,y1:-r,x2:e,y2:r});return{rect:{x:-i,y:n?-n:0,width:2*i,height:2*n},boundaries:[o(-i),o(i)]}}function Kg(e,t){const i=[e[0],e[1]],n=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,n)<=0?{a:i,b:n}:{a:n,b:i}}function Yg(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const n=Kg(e.a,e.b);if(Math.hypot(n.b[0]-n.a[0],n.b[1]-n.a[1])<=1e-9)return;const r=function(e,t){const i=e=>(Math.abs(e)<=5e-10?0:Math.round(1e6*e)/1e6).toFixed(6);return`${i(e[0])},${i(e[1])}>${i(t[0])},${i(t[1])}`}(n.a,n.b),o=t.get(r);if(o)return o.physicalHalfWidth=Math.max(o.physicalHalfWidth,e.half||0),o.sourceOrder=Math.min(o.sourceOrder,i),void(e.partitionHost&&(o.partitionHost&&o.partitionHost.id!==e.partitionHost.id?o.ambiguousPartitionHost=!0:o.partitionHost=e.partitionHost));t.set(r,{segmentKey:r,a:n.a,b:n.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function Xg(e,t){if(Math.abs(e.distance-t.distance)>1e-9)return e.distance-t.distance;if(Math.abs(e.perpendicular-t.perpendicular)>1e-9)return e.perpendicular-t.perpendicular;return(e.target.segmentKeyt.target.segmentKey?1:0)||e.target.sourceOrder-t.target.sourceOrder}function Zg(e){const t=Yg(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,a=(e[0]-t.a[0])*o+(e[1]-t.a[1])*s,l=Math.max(0,Math.min(r,a)),c=t.a[0]+l*o,h=t.a[1]+l*s;return{along:l,length:r,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*s-(e[1]-t.a[1])*o)}}(e.pointer,t);return{target:t,...i,envelope:Math.max(e.baseTolerance,t.physicalHalfWidth+e.bodyPointerPadding)}}).filter(e=>e.distance<=e.envelope+1e-9).filter(t=>!function(e,t,i,n,r){const o=t.b[0]-t.a[0],s=t.b[1]-t.a[1],a=Math.hypot(o,s);if(!(a>1e-9))return!1;const l=o/a,c=s/a;for(const o of i){if(!o.open&&o.kind)continue;const i=Kg(o.a,o.b),s=i.b[0]-i.a[0],a=i.b[1]-i.a[1],h=Math.hypot(s,a);if(!(h>1e-9))continue;const d=s/h,u=a/h;if(Math.abs(l*u-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>r)continue;const p=(e[0]-i.a[0])*d+(e[1]-i.a[1])*u,m=1e-9;if(!(p<=m||p>=h-m)&&Math.abs((e[0]-i.a[0])*u-(e[1]-i.a[1])*d)<=n+1e-9)return!0}return!1}(e.pointer,t.target,e.intervals,t.envelope,Math.max(1e-9,Math.min(e.baseTolerance,.04*e.gridStep)))),i=t.filter(t=>!!t.target.partitionHost&&e.renderedLength+2*t.target.physicalHalfWidth>t.length+1e-9).sort(Xg),n=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(Xg);let r=n[0];if(!r)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=n.filter(e=>Math.abs(e.distance-r.distance)<=1e-9&&Math.abs(e.perpendicular-r.perpendicular)<=1e-9),s=o.filter(e=>e.target.partitionHost);if(s.length){if(1!==new Set(s.map(e=>e.target.partitionHost.id)).size||s.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=s[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],n=Math.hypot(t,i),a=t/n,l=i/n;if(!o.every(t=>{const i=t.target.b[0]-t.target.a[0],n=t.target.b[1]-t.target.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,c=Math.abs(a*s-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*a);return c&&h<=1e-9}))return{candidate:null,jambBlockedTarget:null};r=e}if(r.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:a,length:l}=r,c=a.b[0]-a.a[0],h=a.b[1]-a.a[1],d=c/l,u=h/l,p=a.partitionHost?a.physicalHalfWidth:0,m=Math.min(Math.max(0,e.renderedLength)/2+p,l/2);let _=r.along;const g=Math.max(e.gridStep,1e-9),f=l/2;_=Math.abs(_-f)<=g/2?f:Math.round(_/g)*g,_=Math.max(m,Math.min(l-m,_));const v=a.a[0]+d*_,y=a.a[1]+u*_,b=Math.max(0,e.renderedLength)/2,w=_-b,k=_+b,x=Math.max(0,w),$=Math.max(0,l-k),S=[a.a[0]+d*(w-x/2),a.a[1]+u*(w-x/2)],M=[a.a[0]+d*(k+$/2),a.a[1]+u*(k+$/2)];let C=180*Math.atan2(h,c)/Math.PI;C>=90?C-=180:C<-90&&(C+=180);const R=Math.abs(_-f)<=1e-9;return{candidate:{presetRevision:e.preset.revision,geometryRevision:e.geometryRevision,pointer:[e.pointer[0],e.pointer[1]],type:e.preset.type,lengthCm:e.preset.lengthCm,flipH:e.preset.flipH,flipV:e.preset.flipV,x:v,y:y,angle:C,renderedLength:e.renderedLength,target:a,...a.partitionHost?{host:{...a.partitionHost,t:l>0?_/l:0}}:{},measure:{labels:[{distance:x,midpoint:S},{distance:$,midpoint:M}],guide:R?{x:v,y:y,angle:C}:null}},jambBlockedTarget:null}}function Jg(e,t,i,n,r=1e-6){return e.presetRevision===i&&e.geometryRevision===n&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=r}function Qg(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}let ef=0;const tf="1.72.0-beta.2",nf="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91",rf=1500,of=new Map;let sf=0,af=1e4;const lf="houseplan_card_layout_v1",cf="houseplan_card_cfg_v1",hf="houseplan_card_zoom_v1",df="houseplan_card_nav_v1",uf="houseplan_card_kiosk_v1",pf="houseplan_card_view_v1",mf="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",_f=1e3,gf=(e,t)=>{const i=Math.min(e.x,t.x),n=Math.min(e.y,t.y);return{x:i,y:n,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-n}},ff=(e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)},vf=e=>{history.pushState(null,"",e),ff(window,"location-changed",{replace:!1})},yf=(e,t)=>{let i,n=null;const r=(...r)=>{clearTimeout(i),n=r,i=window.setTimeout(()=>{i=void 0;const t=n;n=null,t&&e(...t)},t)};return r.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=n;n=null,t&&e(...t)},r.cancel=()=>{clearTimeout(i),i=void 0,n=null},r.pending=()=>void 0!==i,r},bf=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}},wf=["edges","corners"];class kf extends he{constructor(){super(...arguments),this._editorRuntime=null,this._onboardingRuntime=null,this._editorRuntimeLoadingVisible=!1,this._backdropGuard=null,this._editorModeRequest=0,this._warmModeRequest=0,this._editorRuntimeLoader=new mg({expectedFingerprint:nf,load:async e=>{const t=0===e?await import("./houseplan-editor-runtime-CaF5LJAh.js"):await import((()=>{const e=new URL("./houseplan-editor-runtime-CaF5LJAh.js",import.meta.url);return e.searchParams.set("hp_retry",`${tf}-${++ef}`),e.href})());return{fingerprint:t.EDITOR_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanEditorRuntime(this)}},install:e=>{this._editorRuntime=e},stateChanged:e=>this._editorRuntimeStateChanged(e),failed:(e,t)=>{console.error("[houseplan] unable to load editor runtime",e),this._showToast(ug(e=>this._t(e),t))}}),this._onboardingRuntimeLoader=new mg({expectedFingerprint:nf,load:async e=>{const t=0===e?await import("./houseplan-onboarding-runtime-BiIOObYb.js"):await import((()=>{const e=new URL("./houseplan-onboarding-runtime-BiIOObYb.js",import.meta.url);return e.searchParams.set("hp_retry",`${tf}-${++ef}`),e.href})());return{fingerprint:t.ONBOARDING_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanOnboardingRuntime(this)}},install:e=>{this._onboardingRuntime=e},failed:(e,t)=>{console.error("[houseplan] unable to load onboarding runtime",e),this._showToast(ug(e=>this._t(e),t))}}),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._cfgContentFingerprint="",this._unsubCfg=null,this._unsubLayout=null,this._unsubVirtual=null,this._liveSyncAttempt=null,this._liveSyncGeneration=0,this._liveSyncConnection=null,this._layoutRev=0,this._layoutContentFingerprint="",this._virtualLights=Kc(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._areaRelocationIds=new Set,this._areaSnapshotCleanupCandidates=new Map,this._areaRelocationSyncKey="",this._areaRelocationWrite=Promise.resolve(),this._tip=null,this._hoverRoom=null,this._pointerModality=new p_(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...rr},this._decorStyleSeeded=!1,this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._decorImagePalette=null,this._decorAssetCatalog=[],this._decorAssets=new Map,this._decorAssetBusy=!1,this._furnCategory=null,this._furnPreviewInput=null,this._furnTouchPending=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this._cancelDevicePressFeedback(),e.matches&&this._cancelCameraTransition(!0),e.matches&&this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:e.matches&&this._modeTransition.active&&this._cancelModeTransition(!0),this.requestUpdate()},this._editorChromeMode="plan",this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest=0,this._modeTransitionTargetZoom=1,this._modeTransitionEditorCamera=null,this._modeTransition=new q_({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._cameraTransitionFit=null,this._cameraTransition=new X_({frame:e=>this._applyCameraTransitionFrame(e),settled:e=>this._settleCameraTransition(e)}),this._editorSecondaryCopy={groupActive:(e,t)=>this._t("editor.group_active",{group:e,item:t}),openGroup:e=>this._t("editor.open_group",{group:e}),disabledAction:(e,t)=>this._t("editor.disabled_action",{action:e,reason:t})},this._tool="draw",this._geometryHistory=new M_(50),this._devicePositionHistory=new M_(50),this._devicePositionBusy=!1,this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._partitionDeleteDialog=null,this._roomDeleteDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCacheValue=null,this._wallUnionPool=new Map,this._isoGeometryCache=new Map,this._isoFallback=new Set,this._openingTunnelCache=null,this._openingWallIndexCache=new Map,this._openingPlacementIntervalsCache=null,this._openingDimensionContextCache=null,this._planSnapGeometryCache=null,this._planStructuralGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._physicalBodiesCache=null,this._lightPhysicalBodiesCache=null,this._cleanFloorCache=new Map,this._innerContourCache=new Map,this._glowRuntimeState=Cm(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._roomFocus=null,this._roomPointer=null,this._doubleFit=new lg,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._touchContacts=new Map,this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._connectedPath="",this._routeDepartureHandled=!1,this._onLocationChanged=()=>{this._connectedPath&&location.pathname!==this._connectedPath?this._leaveCardRoute():location.pathname===this._connectedPath&&(this._routeDepartureHandled=!1)},this._touchGestureGuard={capture:!0,handleEvent:e=>this._guardTouchGesture(e)},this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootSettling=!1,this._bootSettleRaf=0,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._dangerConfirm=null,this._dangerConfirmController=new ge(e=>{this._dangerConfirm=e}),this._confirmDanger=e=>this._config&&this.hass?"warm"===this._syncDangerConfirmLocaleGate()||this._dangerConfirmMissingSpace()?Promise.resolve(!1):this._dangerConfirmController.confirm(e):Promise.resolve(!1),this._cancelDangerConfirm=()=>{this._dangerConfirmController.cancel()},this._onDangerConfirmDecision=e=>{this._dangerConfirmController.resolve(e.detail.token,e.detail.accepted)},this._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._haSupportApi=null,this._haDecorAssetsApi=null,this._decorAssetSyncToken=0,this._alignDialog=null,this._settingsDialog=null,this._supportDialog=null,this._backupExportDialog=null,this._backupImportDialog=null,this._sunRaysCache=null,this._dayCycleTimer=0,this._dayCycleClockKey="",this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._nativeMoreInfoEntity=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++sf,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={alpha:!1,active:Object.freeze([]),space:""},this._viewPreference={},this._renderProjection="flat",this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotLayout=null,this._capturedSnapshotActivity="",this._capturedSnapshotConfigEpoch=-1,this._capturedSnapshotVirtual="",this._lastValidStageSize=null,this._pendingRefitSize=null,this._refitRaf=0,this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"===e.kind)return this._clearRoomFocus(!0),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._cancelCameraTransition(!0),void(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0));if(this._vacJumpOnce=!0,!e.long){const e=Date.now();let t=!1;for(const i of this._activityRt.values())!i.flashKind||(i.expiresAt||i.flashTs+l_)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&this.requestUpdate())}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._resumeSettling=!0,this._loading?this.requestUpdate():this._loadFromServer()},this._resumeSettling=!1,this._viewportInvalidAt=0,this._vacFit=null,this._vacAllCamerasFor=null,this._vacAllCameraCache=null,this._vacCalConfirm=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._tabDrag=null,this._tabDragRelease=null,this._tabSuppressClick=!1,this._tabOrderWarned=!1,this._onLabsSnapshot=e=>{const t=this._effectiveProjection();this._labs=e;const i=this._effectiveProjection();this._convertProjectionView(t,i),this.requestUpdate()},this._onHashChange=()=>{if(this._hasFixedFloor)return;const e=this._hashSpace();if(e&&this._model.find(t=>t.id===e)&&e!==this._space){if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._selId=null,this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate()}},this._drag=null,this._deviceDrag=null,this._rlResize=null,this._holdFired=!1,this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._loading||this._loadFromServer()},this._cfgEpoch=0,this._cfgEpochPreservedConfig=null,this._modelCache=null,this._emptySpaceStateActive=!1,this._decorSnapCache=null,this._markerPreviewMemo=null,this._markerPreviewDevicesMemo=null,this._showHidden=!1,this._connHooked=null,this._connectionWasLost=!1,this._haRegistryConnection=null,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._onHaRegistryUpdate=()=>{const e=ph(this.hass);e.revision===this._haRegistryRev&&this._devices.length||(this._haRegistryRev=e.revision,this._planHassMemo=null,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())},this._onConnReady=()=>{if(this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,uh(this.hass),this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(this._onConnReady,400));!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._loading||this._loadFromServer()},this._onConnLost=()=>{this._booting&&!this._continuity.hasCompleteFrame||(this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost())},this._signer=new Hl(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=yf(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=Qr(this._layout[t]);e&&(this._layout={...this._layout,[t]:e},this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(e=>this._noteLayoutRev(e)).catch(e=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)}))}this._cacheSnapshot()}else this._persistLocalLayout()},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>this._editorRuntimeOrThrow()._undoGeometry(),this._redoGeometry=()=>this._editorRuntimeOrThrow()._redoGeometry(),this._saveConfigDebounced=yf(()=>{this._serverCfg&&this._writeConfig().catch(e=>{const t=!0===e?.physicalGeometryRolledBack;"geometry-unsafe"!==e?.code&&("wall_model_client_outdated"===e?.code?this._showToast(this._t("toast.wall_model_client_outdated")):"conflict"===e?.code?(this._showToast(this._t("toast.conflict")),t||(this._cancelPath(),this._reloadConfigOnly(!0))):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)})),t&&this._reloadRejectedPhysicalWrite())})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>this._editorRuntimeOrThrow()._savePhysicalDialog(),this._deletePhysicalSelection=()=>this._editorRuntimeOrThrow()._deletePhysicalSelection(),this._confirmPartitionDelete=()=>this._editorRuntimeOrThrow()._confirmPartitionDelete(),this._deleteDraftWhole=()=>this._editorRuntimeOrThrow()._deleteDraftWhole(),this._deleteDraftSegment=()=>this._editorRuntimeOrThrow()._deleteDraftSegment(),this._confirmRoomDelete=e=>this._editorRuntimeOrThrow()._confirmRoomDelete(e),this._rebindPartitionOpening=()=>this._editorRuntimeOrThrow()._rebindPartitionOpening(),this._keepClosedAsPartitions=()=>this._editorRuntimeOrThrow()._keepClosedAsPartitions(),this._toggleServerPlans=async()=>this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._toggleServerPlans():this._editorRuntimeOrThrow()._toggleServerPlans(),this._aspectJob=null,this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=qn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(this._editorRuntime)return this._editorRuntime._openSettingsDialog();this._ensureEditorRuntime().then(e=>{e&&this._openSettingsDialog()})},this._openSupportDialog=()=>{this._editorRuntime?this._editorRuntime._openSupportDialog():this._ensureEditorRuntime().then(e=>{e&&this._openSupportDialog()})},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._editorRuntimeOrThrow()._openAlignDialog(),this._toggleOptimizeLivePositions=()=>this._editorRuntimeOrThrow()._toggleOptimizeLivePositions(),this._optimizeUndoBusy=!1,this._openBackupExport=()=>this._editorRuntimeOrThrow()._openBackupExport(),this._openRulesDialog=()=>this._editorRuntimeOrThrow()._openRulesDialog(),this._climateCache=null,this._gearPtCache=new WeakMap}_editorRuntimeOrThrow(){if(!this._editorRuntime)throw new Error("Houseplan editor runtime is not loaded");return this._editorRuntime}async _ensureEditorRuntime(){return this._editorRuntimeLoader.ensure()}async _ensureOnboardingRuntime(){return this._onboardingRuntimeLoader.ensure()}_editorRuntimeStateChanged(e){clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,"loading"!==e?(this._editorRuntimeLoadingVisible=!1,this.requestUpdate()):this._editorRuntimeLoadingTimer=window.setTimeout(()=>{this._editorRuntimeLoadingVisible=!0,this.requestUpdate()},150)}async _requestMode(e,t=!0,i=!1){const n=++this._editorModeRequest;if(i&&(this._warmModeRequest=n,this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._pendingRefitSize=null),"view"===e||await this._ensureEditorRuntime()){if(n===this._editorModeRequest&&this.isConnected)return i?(this._adoptMode(e),this._warmRevivePending&&(clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmReviveDialog()),this.requestUpdate(),void this.updateComplete.then(()=>requestAnimationFrame(()=>requestAnimationFrame(()=>{if(this._warmModeRequest!==n||n!==this._editorModeRequest)return;const e=this._stageEl;this._lastValidStageSize=e&&e.clientWidth>0&&e.clientHeight>0?[e.clientWidth,e.clientHeight]:null,this._pendingRefitSize=null,this._warmModeRequest=0})))):void this._setMode(e,t);this._warmModeRequest===n&&(this._warmModeRequest=0)}else this._warmModeRequest===n&&(this._warmModeRequest=0)}_seedDecorStyle(e){if(this._decorStyleSeeded||!e)return;this._decorStyleSeeded=!0;const t=e.settings?.decor_default_style;t&&(this._decorStyle=function(e,t){if(!e||"object"!=typeof e)return{...t};const i=e,n="string"==typeof i.color&&i.color?i.color:t.color,r="string"==typeof i.fill_color&&i.fill_color?i.fill_color:t.fillColor,o=Number(i.width_cm);return{color:n,opacity:or(i.opacity,t.opacity),widthCm:Number.isFinite(o)&&o>0?Math.max(.1,Math.min(100,o)):t.widthCm,fill:"boolean"==typeof i.fill?i.fill:t.fill,fillColor:r,fillOpacity:or(i.fill_opacity,t.fillOpacity)}}(t,rr))}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}get _modeTransitionBusy(){return this._modeTransitionPreparing||this._modeTransition.active}get _doubleFitEnabled(){return!("view"!==this._mode||this._vacFit||this._modeTransitionBusy||this._touchSequenceMultitouch||"steady"!==this._continuity.state||this._continuity.overlayBlocksInteraction)}_cameraState(){const e=this._viewOr(this._baseVb());return{zoom:this._zoom,viewBox:{...e}}}_normalizeCameraState(e){const t=this._cameraTransitionFit||vi(this._baseVb(),this._stageAspect());return{zoom:Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,e.zoom)),viewBox:this._clampView({...e.viewBox},t)}}_applyCameraTransitionFrame(e){const t=this._normalizeCameraState(e.presented);e.presented=t,this._zoom=t.zoom,this._view={...t.viewBox},this.requestUpdate()}_settleCameraTransition(e){const t=this._normalizeCameraState(e.to);this._zoom=t.zoom,this._view={...t.viewBox},this._cameraTransitionFit=null,"room"!==e.reason&&this._saveZoom(),this.requestUpdate()}_cancelCameraTransition(e=!1,t=!1){const i=this._cameraTransition.state?.reason,n=t&&this._cameraTransition.active?this._cameraTransition.presented?.zoom:void 0;this._cameraTransition.cancel(e),this._cameraTransitionFit=null,void 0!==n&&"room"!==i&&this._saveZoom()}_prepareCameraCommand(){this._modeTransitionBusy&&this._cancelModeTransition(!0),"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1))}_startCameraTransition(e,t,i,n){const r=this._cameraState(),o=this._cameraTransition.target;return(!o||!G_(o,e))&&(G_(r,e)?(this._cancelCameraTransition(!1),!1):(this._cameraTransitionFit={...t},this._cameraTransition.start(r,e,i,this._reducedMotion?0:n),!0))}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const n=document.createElement("span");n.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(n);const r=getComputedStyle(n).color||t;return n.remove(),r}_currentModeVisual(e=this._mode){const t=this._modeTransition.presented||this._modeTransitionVisual;if(t)return{...t,viewport:{...t.viewport,viewBox:{...t.viewport.viewBox}}};const i=this._stageEl;if(!i||i.clientWidth<=0||i.clientHeight<=0)return null;const n=this.renderRoot.querySelector(".editorchrome"),r=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),s=this.renderRoot.querySelector(".hp-backdrop"),a=this.renderRoot.querySelector(".zoomwrap"),l=a?getComputedStyle(a).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:n?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:N_(r,i.clientWidth),stageColor:getComputedStyle(i).backgroundColor||"rgb(255, 255, 255)",paperColor:o?getComputedStyle(o).fill:"rgb(255, 255, 255)",sceneBrightness:Number.isFinite(c)?c:1,architectureOpacity:"decor"===e?.35:1,backdropOpacity:s&&Number(getComputedStyle(s).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,n,r){const o=vi(this._baseVb(),n/Math.max(1,r)),s=Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,e)),a=o.w/s,l=o.h/s,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-a/2,y:h-l/2,w:a,h:l},o)}_targetStageColor(e){return"view"!==e?"rgb(255, 255, 255)":this._cssColor(this._stageBg(this._spaceDisplayForRender()),this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"))}_targetPaperColor(e){return"view"===e&&this._spaceModel()?.bg?this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"):"rgb(255, 255, 255)"}_targetBrightness(e){return 1}_applyModeTransitionFrame(e){e.targetMode===this._mode&&(this._modeTransitionVisual=e.presented,this._view={...e.presented.viewport.viewBox},this.requestUpdate())}_settleModeTransition(e){if(e.targetMode!==this._mode)return;const t=this._modeTransitionRequest;this._view={...e.to.viewport.viewBox},this._zoom=this._modeTransitionTargetZoom,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._lastValidStageSize=[e.to.stageWidth,e.to.stageHeight],"view"===this._mode&&(this._saveZoom(),this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate(),this.updateComplete.then(()=>{if(!this.isConnected||t!==this._modeTransitionRequest||this._modeTransitionBusy||e.targetMode!==this._mode)return;const i=this._stageEl;if(i&&i.clientWidth>0&&i.clientHeight>0&&(Math.abs(i.clientWidth-e.to.stageWidth)>.5||Math.abs(i.clientHeight-e.to.stageHeight)>.5)){const e=this._view;this._lastValidStageSize=[i.clientWidth,i.clientHeight],this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}const n=this.renderRoot.activeElement;(!n||!n.isConnected||!!n.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){this._cancelCameraTransition(!1);const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){e!==this._mode&&this._clearRoomFocus(!0),this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,n){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const r=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,n,r,o),this._lastValidStageSize=[r,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,n,r,o){this.updateComplete.then(()=>{if(!this.isConnected||e!==this._modeTransitionRequest||this._mode!==i)return void(e===this._modeTransitionRequest&&(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this.requestUpdate()));const s=this.renderRoot.querySelector(".editorchrome"),a=s?.querySelector(".editorchrome-inner"),l="view"===i?0:a?.scrollHeight||a?.getBoundingClientRect().height||0,c=Math.max(1,t.stageHeight+t.editorChromeHeight),h=Math.max(1,c-l),d=this._stageEl?.clientWidth||t.stageWidth;if(d<=0||h<=0)return this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._applyView(n,r,o),void this.requestUpdate();const u=this._viewForModeTarget(n,r,o,d,h),p={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:N_(u,d),stageColor:this._targetStageColor(i),paperColor:this._targetPaperColor(i),sceneBrightness:this._targetBrightness(i),architectureOpacity:"decor"===i?.35:1,backdropOpacity:"decor"===i&&"backdrop"!==this._decorTool?.5:1,viewWeight:"view"===i?1:0,editorWeight:"view"===i?0:1,toolbarContentOpacity:"view"===i?0:1};this._modeTransitionPreparing=!1;const m=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,p,i,this._reducedMotion||m?0:220)})}get _hasFixedFloor(){return!!this._config&&Object.prototype.hasOwnProperty.call(this._config,"floor")}_fixedFloorState(e=this._model,t=this._loadOk){const i=this._config?.floor,n=function(e){if(!e.hasFloor)return{kind:"absent"};const t=e.floor;if("string"==typeof t)return t.length?e.spaceIds.includes(t)?{kind:"valid",id:t,source:"id"}:{kind:"invalid",reason:"unknown-id",value:t}:{kind:"invalid",reason:"empty-id",value:t};if("number"==typeof t){if(!Number.isFinite(t))return{kind:"invalid",reason:"non-finite-index",value:t};if(!Number.isInteger(t))return{kind:"invalid",reason:"fractional-index",value:t};if(t<0)return{kind:"invalid",reason:"negative-index",value:t};const i=e.spaceIds[t];return void 0===i?{kind:"invalid",reason:"out-of-range-index",value:t}:{kind:"valid",id:i,source:"index"}}return{kind:"invalid",reason:"invalid-type",value:t}}({spaceIds:e.map(e=>e.id),hasFloor:this._hasFixedFloor,floor:i});return!this._hasFixedFloor||t?n:"number"==typeof i?"valid"===n.kind||"invalid"===n.kind&&"out-of-range-index"===n.reason?{kind:"pending",value:i}:n:"valid"===n.kind||"invalid"===n.kind&&"unknown-id"!==n.reason?n:{kind:"pending",value:i}}_canCommitSpace(e,t=!1){if(t||!this._hasFixedFloor)return!0;const i=this._fixedFloorState();return"valid"===i.kind&&i.id===e}_commitSpace(e,t=!1){return!!this._canCommitSpace(e,t)&&(e!==this._space&&(this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._cancelCameraTransition(!1),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._editorRuntime?._clearFurniturePreview()),this._space=e,!0)}_slideTo(e,t){if(e===this._space)return!0;if(!this._canCommitSpace(e))return!1;if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return!1;this._cancelModeTransition(!0);const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;return this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._editorSecondary?.closeForNavigation(),this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=t,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate()),!0}get _canReorderTabs(){return Qg({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){Qg({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(bf(e),this._tabDragRelease=e=>this._tabPointerUp(e),window.addEventListener("pointerup",this._tabDragRelease),window.addEventListener("pointercancel",this._tabDragRelease),this._tabDrag={id:t,pointerId:e.pointerId,x:e.clientX,y:e.clientY,moved:!1,targetId:null,placement:null})}_tabDropTargetAt(e,t,i){const n=this._model.map(e=>e.id),r=n.indexOf(i);if(r<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const s of o){const o=s.dataset.id||"";if(!o||o===i)continue;const a=s.getBoundingClientRect();if(ea.right||ta.bottom)continue;const l=n.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,n;const r=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===r?.targetId&&t.placement===r?.placement||(this._tabDrag={...t,moved:!0,targetId:r?.targetId||null,placement:r?.placement||null})}_tabPointerUp(e){const t=this._tabDrag;if(t&&t.pointerId!==e.pointerId)return;const i="pointerup"===e.type&&t?.moved?this._tabDropTargetAt(e.clientX,e.clientY,t.id):null;"pointerup"===e.type&&t?.moved&&this._suppressNextTabClick(),this._endTabDrag(),t?.moved&&i&&this._commitTabOrder(t.id,i.targetId)}_endTabDrag(){this._tabDrag=null,this._tabDragRelease&&(window.removeEventListener("pointerup",this._tabDragRelease),window.removeEventListener("pointercancel",this._tabDragRelease),this._tabDragRelease=null)}_tabClick(e){if(this._tabSuppressClick)return this._tabSuppressClick=!1,clearTimeout(this._tabSuppressClickTimer),void(this._tabSuppressClickTimer=void 0);this._pickSpace(e)}_suppressNextTabClick(){this._tabSuppressClick=!0,clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=window.setTimeout(()=>{this._tabSuppressClick=!1,this._tabSuppressClickTimer=void 0},0)}_commitTabOrder(e,t){const i=this._serverCfg;if(!i||!this._canReorderTabs)return;const n=this._model.map(e=>e.id),r=function(e,t,i){const n=e.indexOf(t),r=e.indexOf(i);if(n<0||r<0||n===r)return e;const o=e.slice();return o.splice(n,1),o.splice(r,0,t),o}(n,e,t);if(r===n)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),s=function(e,t,i,n=()=>""){if(!i)return[];const r=[];for(const o of e){if(!o||!0===o.removed)continue;const e="string"==typeof o.id?o.id:"";if(!e)continue;if("string"==typeof o.space&&o.space)continue;const s=("string"==typeof o.area?o.area:"")||n(e)||"";s&&t[s]||r.push({id:e,space:i})}return r}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),n[0]||"",e=>o.get(e)||"");if(s.length){const e=new Map(s.map(e=>[e.id,e.space]));for(const t of i.markers||[]){const i=e.get(String(t.id));i&&(t.space=i)}}i.spaces=function(e,t){const i=new Map(t.map((e,t)=>[e,t]));return e.map((e,t)=>({space:e,index:t})).sort((e,t)=>(i.get(String(e.space?.id))??Number.MAX_SAFE_INTEGER)-(i.get(String(t.space?.id))??Number.MAX_SAFE_INTEGER)||e.index-t.index).map(e=>e.space)}(i.spaces||[],r),this._saveConfig(),this._tabOrderWarned||(this._tabOrderWarned=!0,this._showToast(this._t("toast.space_order_changed")))}_pickSpace(e){if(this._endTabDrag(),e===this._space)return;const t=this._model.map(e=>e.id),i=t.indexOf(this._space),n=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&n0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const e=this._model.map(e=>e.id),t=e.indexOf(this._space);this._slideTo(e[(t+1)%e.length],"left"),this._showKioskDots()}}_syncCycleTimer(){clearInterval(this._cycleTimer),this._cycleTimer=void 0,this.isConnected&&!this._hasFixedFloor&&this._config?.kiosk&&Number(this._config.cycle)>0&&(this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle)))}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}get _wallUnionCache(){return this._wallUnionCacheValue}set _wallUnionCache(e){this._wallUnionCacheValue=e,null===e&&this._wallUnionPool.clear()}get _glowClipCache(){return this._glowRuntimeState.clipCache}get _glowGeometryWarnings(){return this._glowRuntimeState.geometryWarnings}get _glowFeatherUnits(){return this._glowRuntimeState.featherUnits}set _glowFeatherUnits(e){this._glowRuntimeState.featherUnits=e}get _glowRenderedSources(){return this._glowRuntimeState.renderedSources}get _glowLastAppearance(){return this._glowRuntimeState.lastAppearance}get _glowFeatherSuspendUntil(){return this._glowRuntimeState.featherSuspendUntil}set _glowFeatherSuspendUntil(e){this._glowRuntimeState.featherSuspendUntil=e}get _glowFeatherResumeTimer(){return this._glowRuntimeState.featherResumeTimer}set _glowFeatherResumeTimer(e){this._glowRuntimeState.featherResumeTimer=e}get _glowSourceSeq(){return this._glowRuntimeState.sourceSeq}set _glowSourceSeq(e){this._glowRuntimeState.sourceSeq=e}_syncDangerConfirmLocaleGate(){return this._config&&this.hass?hu(this,_u,wu(this.hass,this._config.language)):"ready"}_dangerConfirmMissingSpace(){const e=this._model;if(!e.length)return!1;const t=this._fixedFloorState(e);return"pending"!==t.kind&&"invalid"!==t.kind&&!this._spaceModel()}_newContinuityController(){return new Ol(()=>{this._resumeSettling="steady"!==this._continuity.state,this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}get _labsIso(){return this._labs.active.includes("iso")}get _desiredProjection(){return"view"===this._mode&&this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat"}_saveViewPreference(){try{localStorage.setItem(pf,JSON.stringify(this._viewPreference))}catch{}}_logicalViewCenter(e){const t=this._view;if(!t)return null;const i=[t.x+t.w/2,t.y+t.h/2],n="iso"===e?tg(i):i;return{x:n[0],y:n[1]}}_convertProjectionView(e,t){if(e===t)return;this._clearRoomFocus(!0);const i=this._logicalViewCenter(e);this._view=null;const n=i?"iso"===t?eg([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,n?.[0],n?.[1]),this._warmPatch({vp:this._warmViewportState()}),this.requestUpdate()}_setProjection(e){if(!this._labsIso||"view"!==this._mode)return;const t=this._effectiveProjection();if(this._viewPreference={...this._viewPreference,[this._space]:e},"iso"===e){const e=this._isoSceneKey();e&&this._isoFallback.delete(e)}this._saveViewPreference();const i=this._effectiveProjection();this._convertProjectionView(t,i)}_hashSpace(){return xg(window.location.hash||"")}connectedCallback(){this._connectedPath=location.pathname,this._routeDepartureHandled=!1,window.addEventListener("location-changed",this._onLocationChanged),window.addEventListener("popstate",this._onLocationChanged),this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1);const e=v_(this.ownerDocument);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=Pl(this.ownerDocument,this._pageVisibility),this._languageFailureUnsub?.(),this._languageFailureUnsub=(t=()=>{this._showToast(this._t("toast.locale_load_failed"))},mu.add(t),()=>mu.delete(t)),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView);const i=this.ownerDocument.defaultView?.MutationObserver;i&&(this._pointerHoverObserver=new i(e=>{for(const t of e)for(const e of t.addedNodes)this._syncPointerHoverSubtree(e)}),this._pointerHoverObserver.observe(this.renderRoot,{childList:!0,subtree:!0})),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),f_(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())}),this.hass&&this._ensureHaRegistryAuthority(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>this._referencedContentUrls()),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e){Rg.add(e);const t=zg();return"undefined"==typeof window&&e(t),()=>{Rg.delete(e)}}(this._onLabsSnapshot),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},rf)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._loadOk&&this._ensureLiveSyncSubscriptions(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0)),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._connectedPath&&location.pathname!==this._connectedPath&&this._leaveCardRoute(),window.removeEventListener("location-changed",this._onLocationChanged),window.removeEventListener("popstate",this._onLocationChanged),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._languageFailureUnsub?.(),this._languageFailureUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._warmModeRequest=0,this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._bootSettleRaf&&(cancelAnimationFrame(this._bootSettleRaf),this._bootSettleRaf=0),this._bootSettling=!1;for(const e of this._activityRt.values())clearTimeout(e.timer);window.removeEventListener("keydown",this._keyHandler),this._endTabDrag(),clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=void 0,this._tabSuppressClick=!1,clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null,this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,this._editorRuntimeLoadingVisible=!1,this._modeTransition.dispose(),this._cameraTransition.dispose(),this._cameraTransitionFit=null,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest++,this._slideTimer=void 0,this._slide="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),this._cancelDeviceDrag(),window.removeEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=void 0,clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),this._unsubVirtual&&(this._unsubVirtual(),this._unsubVirtual=null),this._unsubTrail&&(this._unsubTrail(),this._unsubTrail=void 0),this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=null,clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),Lm(this._glowRuntimeState,this._glowRuntimeHost),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._touchContacts.clear(),this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._pointerHoverObserver?.disconnect(),this._pointerHoverObserver=void 0,this._pointerModality.disconnect(),this._editorSecondary?.reset(),this._resumeSettling=!1,this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_onKey(e){if("Escape"===e.key&&this._vacFit){if(this._vacFit.busy)return;return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void e.stopPropagation()}if("Escape"===e.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._vacCalConfirm)return void(this._vacCalConfirm.busy||(this._vacCalConfirm=null));if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void this._closeInfoCard();if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return this._alignDialog=null,void(this._preflightClipboardFallback=null);if(this._backupImportDialog)return void(this._backupImportDialog=null);if(this._backupExportDialog)return void(this._backupExportDialog=null);if(this._supportDialog)return void this._editorRuntime?._closeSupportDialog();if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void this._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary?.hasOpenGroup)return e.preventDefault(),void this._editorSecondary?.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),n=t.some(e=>e?.classList?.contains?.("editor-secondary")),r=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),s=/^[a-z]$/.test(o),a="z"===o||!s&&"KeyZ"===e.code,l="y"===o||!s&&"KeyY"===e.code,c=r&&(a&&e.shiftKey||l),h=r&&a&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry();if(("Delete"===e.key||"Backspace"===e.key)&&this._decorSel&&!i&&!n)return e.preventDefault(),void this._decorDeleteSel();const t="ArrowLeft"===e.key?[-this._gridPitch,0]:"ArrowRight"===e.key?[this._gridPitch,0]:"ArrowUp"===e.key?[0,-this._gridPitch]:"ArrowDown"===e.key?[0,this._gridPitch]:null;return!t||r||e.altKey||"select"!==this._decorTool||!this._decorSel||!this._decorList.some(e=>e.id===this._decorSel)||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag||i||n||this._editorSecondaryDialogBlocked?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._furnPalette=null,this._furnCategory=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorNudge(t[0],t[1]))}if("devices"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoDevicePosition()):h?(e.preventDefault(),void this._undoDevicePosition()):void("Escape"===e.key&&this._deviceDrag&&(e.preventDefault(),this._cancelDeviceDrag()))}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!n)return e.preventDefault(),void this._deletePhysicalSelection();if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():this._wallFaceBatch?(this._roomDialogCancel(),void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint())):"draw"===this._tool&&this._path.length?void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint()):"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry();if("Escape"===e.key)return this._physicalDrag||this._physicalRotate?(e.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(e.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length?(e.preventDefault(),void this._finishWallChain()):this._physicalSel?(e.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():("exit-tool"===this._resize?.escapeIdle()&&(this._tool="draw"),void this.requestUpdate())):"split"===this._tool?(e.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(e.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(e.preventDefault(),void(this._wallDialog=null)):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"column"!==this._tool||(e.preventDefault(),"opening"===this._tool&&this._clearOpeningPlacement(!0),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const e=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const t=this._curSpaceCfg,i=(t.room_drafts||[]).findIndex(e=>e.id===this._activeDraftId);if(i>=0){const n=Array.isArray(t.room_drafts[i]?.segments)?t.room_drafts[i].segments:[];this._path.length<2?(t.room_drafts.splice(i,1),t.room_drafts.length||delete t.room_drafts,this._activeDraftId=null):t.room_drafts[i]={id:this._activeDraftId,points:this._path.map(e=>[e[0]/_f,e[1]/_f]),segments:this._draftSegmentCms.map((e,t)=>({...n[t]||{},cm:e}))},this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),e)}return}this._path=this._path.slice(0,-1)}}_undoActiveDraftPoint(){const e=this._activeDraftId,t=this._path[0]?[...this._path[0]]:null,i=this._geometryHistory.undo();if(!i)return void this._undoPoint();if(!this._applyGeometryState(i.before,!0))return void this._geometryHistory.clear();const n=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;n?(this._activeDraftId=n.id,this._path=n.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,n.segments.map(e=>e.cm),n.id),this._resumeDraftBySpace[this._space]=n.id):(this._activeDraftId=null,this._path=t?[t]:[],this._draftSegmentCms=[],e&&this._resumeDraftBySpace[this._space]===e&&delete this._resumeDraftBySpace[this._space]),this._clearPlanSnapHover(),this._showToast(this._t("history.undone",{name:i.name}))}static async getConfigElement(){return await import("./editor-CrMXGx3s.js"),document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of of.values())for(const t of e)clearTimeout(t.evict);of.clear(),af=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const n of of.values())for(const r of n)e++,r.dlg&&(t++,i.push(r.dlg.kind));return{keys:of.size,slots:e,dlgs:t,drafts:i}}setConfig(e){const t=this._config,i=!!t&&Object.prototype.hasOwnProperty.call(t,"floor");this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...e};(i!==this._hasFixedFloor||t?.floor!==this._config.floor)&&(this._hashApplied=!1,this._navApplied=!1,this._warmVpArmed=!1),this._config.kiosk&&(this._booting=!1,this._bootFading=!1),!this._hasFixedFloor&&e.default_floor&&this._commitSpace(e.default_floor,!0);try{this._zoomBySpace=JSON.parse(localStorage.getItem(hf)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(pf)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=zg();try{const e=JSON.parse(localStorage.getItem(uf)||"null");this._kioskScale={icon:kn(e?.icon),font:kn(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(cf)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._seedDecorStyle(this._serverCfg),this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||Il(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Il(this._layout),this._virtualLights=Kc(e.virtual_lights,this._cfgRev),this._serverStorage=!0)}catch{}this._adoptInitialSpace(this._model,this._loadOk),"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._syncCycleTimer(),this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const e=vg(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),n=of.get(e);if(!n||!n.length)return;const r=n.find(e=>e.owner===this._warmGen);if(r)return this._warmLongReturn=!!r.freed&&Date.now()-r.freed>=Dl,clearTimeout(r.evict),r.evict=0,r.freed=0,r.live=!0,this._warmSlot=r,this._warmKey=e,r.frameFingerprint&&this._continuity.adoptCompleteFrame(r.frameFingerprint),void(!this._devices.length&&r.devices?.length&&(this._devices=[...r.devices]));const{slot:o,sure:s}=((e,t,i,n)=>{const r=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===n?4:e.live?0:t?3:2};let o=null,s=0,a=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=r(i);e<=0||(e>s?(o=i,s=e,a=1):e===s&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(n,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Dl,this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},rf)),this._warmKey=e,s?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=t?new WeakRef(t):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,o.frameFingerprint&&this._continuity.adoptCompleteFrame(o.frameFingerprint),!this._devices.length&&o.devices?.length&&(this._devices=[...o.devices]),this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,path:location.pathname,place:t?new WeakRef(t):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.stageH,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0},n.push(this._warmSlot),this._warmTrim(n)))}_warmIdx(e){const t=e?.children;if(!t)return-1;for(let e=0;e4;){const t=e.findIndex(e=>!e.live);if(t<0)break;clearTimeout(e[t].evict),e.splice(t,1)}}_warmScheduleEvict(e,t){if(clearTimeout(e.evict),!(e.dlg||e.vp&&"view"!==e.vp.mode))return;const i=e.freed,n=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==n)return;var r;e.dlg=null,e.vp=(r=e.vp)&&"view"!==r.mode?{...r,mode:"view",zoom:r.snap?.space===r.space?r.snap.zoom:r.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:r,e.frameFingerprint="";const o=of.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},af+250)}_warmAdoptViewport(e){const t=this._warmVp;if(!t||this._warmSlot?.path!==location.pathname)return void(this._warmVp=null);const i=this._fixedFloorState();if(this._hashApplied||!this._model.find(e=>e.id===t.space)||"valid"===i.kind&&i.id!==t.space||this._hasFixedFloor&&"valid"!==i.kind)return void(this._warmVp=null);this._commitSpace(t.space,!0),this._navApplied=!0;const n="view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view";this._adoptMode("view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const r=this._effectiveProjection(),o=r===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=o&&t.view?{...t.view}:null,this._viewModeSnap=o&&t.snap?{...t.snap}:null,!o&&t.logicalCenter){const e="iso"===r?eg([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var s;this._tool="opening"===(s=function(e){return"partition"===e?"draw":e}(s=t.tool))?"draw":"string"==typeof s&&fg.has(s)?s:"draw",this._decorTool=t.decorTool,this._showHidden=t.showHidden,this._showFar!==t.showFar&&(this._showFar=t.showFar,this._frame=null),this._selId=t.selId,this._resize?.restoreSelection(t.rszSel),this._decorSel=t.decorSel,this._warmVpArmed=!0,"view"!==n&&this._requestMode(n,!1,!0)}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=vg(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!t)return;const e=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,path:location.pathname,place:e?new WeakRef(e):null,idx:this._warmIdx(e),live:!0,hdrH:this._hdrH,stageH:0,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0};const n=of.get(i)||[];for(n.push(this._warmSlot),of.set(i,n),this._warmTrim(n);of.size>8;){const e=of.keys().next().value;if(void 0===e||e===i)break;for(const t of of.get(e)||[])clearTimeout(t.evict);of.delete(e)}}Object.assign(this._warmSlot,e)}}_warmViewportState(){const e=this._effectiveProjection();return{space:this._space,mode:this._mode,projection:e,activeLabsIso:this._labsIso,logicalCenter:this._logicalViewCenter(e),zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._resize?.selectedRoomId,decorSel:this._decorSel}}_warmDialogState(){const e=(e,t)=>({kind:e,space:this._space,mode:this._mode,data:t});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog||this._backupExportDialog||this._backupImportDialog?null:this._openingInfo?e("openingInfo",this._openingInfo.id):this._infoCard?e("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:e("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:e("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:e("marker",this._markerDialog):this._openingDialog?e("opening",this._openingDialog):this._backdropDialog?e("backdrop",this._backdropDialog):this._decorShapeDialog?e("decorShape",this._decorShapeDialog):this._decorTextDialog?e("decorText",this._decorTextDialog):this._roomDialog?e("room",{editId:this._roomEditId,fill:this._roomFill,customFill:this._roomCustomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,wallFaceBatch:this._wallFaceBatch,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:e("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._config?.kiosk||"steady"!==this._continuity.state)return;const e={vp:this._warmViewportState(),frameFingerprint:this._continuity.frameFingerprint,devices:this._devices};if(this._warmRevivePending||(e.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const t=this.parentNode;e.place=t?new WeakRef(t):null,e.idx=this._warmIdx(t)}this._warmPatch(e)}_warmReviveDialog(){const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return void(this._warmRevivePending=!1);const t=e.dlg,i=e.freed;if(t.mode===this._mode||"view"===t.mode||this._warmVp?.mode!==t.mode||this._editorRuntime){if(this._warmRevivePending=!1,e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>af)&&t.space===this._space&&t.mode===this._mode){switch(t.kind){case"space":this._spaceDialog={...t.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...t.data,busy:!1};break;case"settings":this._settingsDialog={...t.data,busy:!1};break;case"rules":this._rulesDialog={...t.data,busy:!1};break;case"opening":this._openingDialog={...t.data};break;case"backdrop":this._backdropDialog={...t.data};break;case"decorShape":this._decorShapeDialog={...t.data};break;case"decorText":{this._decorTextDialog={...t.data};const e=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:e,end:e};break}case"room":{const e=t.data;this._roomEditId=e.editId,this._roomFill=e.fill,this._roomCustomFill=e.customFill||null,this._roomTempSrc=e.tempSrc,this._roomHumSrc=e.humSrc,this._roomSrcOpen=e.srcOpen,this._roomSrcFilter=e.srcFilter,this._roomNameScale=e.nameScale,this._roomLabelScale=e.labelScale,this._areaSel=e.areaSel,this._nameSel=e.nameSel,this._pendingSplit=e.pendingSplit,this._wallFaceBatch=e.wallFaceBatch||null,this._path=e.path,this._wallFaceBatch&&(this._activeDraftId=this._wallFaceBatch.activeDraftId,this._draftSegmentCms=[...this._wallFaceBatch.activeCms]),this._roomDialog=!0;break}case"info":{const e=this._devices.find(e=>e.id===t.data);e&&(this._infoCard=e);break}case"openingInfo":{const e=(this._curSpaceCfg?.openings||[]).find(e=>e.id===t.data);e&&(this._openingInfo=e);break}}this.requestUpdate()}}else this._warmRevivePending=!0}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=Il(this._serverCfg),this._layoutContentFingerprint=Il(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const n=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(Jc).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>n.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(cf,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,config_fingerprint:this._cfgContentFingerprint,layout:this._layout,layout_rev:this._layoutRev,layout_fingerprint:this._layoutContentFingerprint,virtual_lights:Xc(this._virtualLights)}))}catch{}}_beginContinuityCandidate(e,t,i="plan"){return this._booting&&!this._continuity.hasCompleteFrame?this._continuity.token:(this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._resumeSettling=!0,this._continuity.beginCandidate(e,i))}_continuityStageValid(){const e=this._stageEl;return!!e&&e.clientWidth>0&&e.clientHeight>0}_continuityAssetsReady(){if(!this._model.length)return!1;const e=this._model.length?this._spaceModel():null;return!e?.bg?.href||this._signer.isReady(this.hass,e.bg.href)}_initialSpaceSelection(e,t=this._loadOk){const i=this._fixedFloorState(e,t);return"valid"===i.kind?{id:i.id,source:"fixed"}:this._hasFixedFloor?{id:null,source:"none"}:function(e){const t=new Set(e.spaceIds.filter(e=>!!e));if(!t.size)return{id:null,source:"none"};const i=[["hash",!1===e.acceptHash?null:e.hashSpace],["current",e.preserveCurrent?e.currentSpace:null],["saved",e.savedSpace],["default",e.defaultSpace],["first",e.spaceIds[0]]];for(const[e,n]of i)if(n&&t.has(n))return{id:n,source:e};return{id:null,source:"none"}}({spaceIds:e.map(e=>e.id),hashSpace:this._hashSpace(),acceptHash:!this._hashApplied,currentSpace:this._space,preserveCurrent:this._hashApplied||this._navApplied||this._warmVpArmed,savedSpace:this._savedNav()?.space,defaultSpace:this._config?.default_floor})}_adoptInitialSpace(e,t=this._loadOk){const i=this._initialSpaceSelection(e,t);return i.id?(this._commitSpace(i.id,!0),"hash"===i.source&&(this._hashApplied=!0),"saved"===i.source&&(this._navApplied=!0),i):i}_candidateBackdrop(e,t=this._space){const i=Ta(e),n=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==n.kind)return"";const r=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===r)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return El([this._cfgRev,this._cfgContentFingerprint||Il(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Il(this._layout),this._space,this._mode,this._view,t,this._glowScreenBlend?"screen":"normal",this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_settleContinuityFrame(){if(this._booting||!this._continuityStageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state)return void(this._continuityAssetsReady()&&(this._renderSnapshotAt=Date.now(),this._continuity.markCompleteFrame(this._visualFrameFingerprint())));if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._continuityStageValid(),assetsReady:()=>this._continuityAssetsReady(),frameFingerprint:()=>this._visualFrameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._resumeSettling=!1,this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._warmSnapshot()):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}_onBackdropLoaded(e,t){this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return V;const e="connection"===this._continuity.recoveryReason;return W`
{}).then(t)}function M_(e,t,i,n,r){return{previous:JSON.parse(JSON.stringify(e)),previousFingerprint:i,revision:n,attempted:t,attemptedFingerprint:r(t)}}function C_(e,t,i){const n=e._serverCfg;return!(!n||e._cfgRev!==t.revision||i(n)!==t.attemptedFingerprint)&&(e._serverCfg=t.previous,e._cfgContentFingerprint=t.previousFingerprint,e.requestUpdate(),!0)}class R_{constructor(e=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(e))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(e){this._undo.push(e),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const e=this._undo.pop()??null;return e&&this._redo.push(e),e}redo(){const e=this._redo.pop()??null;return e&&this._undo.push(e),e}removeWhere(e){const t=this._undo.length,i=this._redo.length;return this._undo=this._undo.filter(t=>!e(t)),this._redo=this._redo.filter(t=>!e(t)),t+i-this._undo.length-this._redo.length}clear(){this._undo=[],this._redo=[]}}function T_(e,t){const i=e[t];return i&&Number.isFinite(i.x)&&Number.isFinite(i.y)?{x:i.x,y:i.y,..."string"==typeof i.s?{s:i.s}:{}}:null}function D_(e,t,i){if(null===i){if(!(t in e))return e;const i={...e};return delete i[t],i}const n={...e[t]||{}};delete n.x,delete n.y,delete n.s;const r={...n,x:i.x,y:i.y,...void 0!==i.s?{s:i.s}:{}};return{...e,[t]:r}}class z_{constructor(){this.seen=null,this.diagnosticsCache=null}observe(e,t,i,n){this.invalidateDiagnosticsForHass(e,t,i),this.intake(t,n)}intake(e,t){e&&this.seen!==e&&(this.seen=e,t())}invalidate(){this.diagnosticsCache=null}invalidateDiagnosticsForHass(e,t,i){this.diagnosticsCache&&(e?.entities!==t?.entities||e?.devices!==t?.devices||[...i?.entityIds||[]].some(i=>!!e?.states?.[i]!=!!t?.states?.[i]))&&this.invalidate()}diagnostics(e,t,i){if(this.diagnosticsCache)return this.diagnosticsCache;const n={active:0,ha_disabled:0,orphaned:0,unverified:0};for(const e of t)e.removed||"virtual"===e.binding||n[i(e.binding).kind]++;return this.diagnosticsCache={registry:xh(e),bindings:n},this.diagnosticsCache}}function A_(e){const t=e;t._hassSequence++,t._renderSnapshotAt=Date.now(),t._continuity.note("hass-snapshot"),t._ensureHaRegistryAuthority(),t._planHassMemo=null,t._hookConnection(),!t._loadOk&&!t._loading&&t._loadTries<8&&t._loadFromServer(),t._maybeRebuildDevices(),t._vacTick(),t._activityTick()}const P_=Object.freeze({enabled:!1,z2mBaseTopics:Object.freeze([])});function F_(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^\/+|\/+$/g,"").replace(/\/{2,}/g,"/");return!t||t.length>180||/[#+\u0000-\u001f\u007f]/.test(t)?null:t}function O_(e){const t=e?.zigbee_topology;if(!t||"object"!=typeof t)return P_;const i=t,n=Array.isArray(i.z2m_base_topics)?i.z2m_base_topics:Array.isArray(i.z2mBaseTopics)?i.z2mBaseTopics:[],r=new Set,o=[];for(const e of n){const t=F_(e);if(t&&!r.has(t)&&(r.add(t),o.push(t),8===o.length))break}return{enabled:!0===i.enabled,z2mBaseTopics:o}}function I_(e,t){const i={...e},n=t.z2mBaseTopics.map(F_).filter(Boolean);return t.enabled||n.length?i.zigbee_topology={enabled:!0===t.enabled,z2m_base_topics:[...new Set(n)].slice(0,8)}:delete i.zigbee_topology,i}const E_={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},N_=e=>Math.max(0,Math.min(1,e)),H_=(e,t,i)=>e+(t-e)*i,L_=(e,t,i)=>{const n=1-e;return 3*n*n*e*t+3*n*e*e*i+e*e*e},B_=e=>{const t=N_(e);if(0===t||1===t)return t;let i=0,n=1,r=t;for(let e=0;e<12;e++)r=(i+n)/2,L_(r,.2,.2)0&&e.w>0?t/e.w:1,viewBox:{...e}}}function W_(e,t,i){const n=Math.max(1e-9,e.pixelsPerUnit),r=Math.max(1e-9,t/n),o=Math.max(1e-9,i/n);return{x:e.centerX-r/2,y:e.centerY-o/2,w:r,h:o}}function j_(e){const t=e.trim(),i=/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/i.exec(t);if(i)return[Number(i[1]),Number(i[2]),Number(i[3]),null==i[4]?1:Number(i[4])];const n=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return n?[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16),8===n.length?parseInt(n.slice(6,8),16)/255:1]:null}function U_(e,t,i){const n=j_(e),r=j_(t);if(!n||!r)return i<.5?e:t;const o=N_(i);return`rgba(${Math.round(H_(n[0],r[0],o))}, ${Math.round(H_(n[1],r[1],o))}, ${Math.round(H_(n[2],r[2],o))}, ${H_(n[3],r[3],o).toFixed(3)})`}class V_{constructor(e,t=E_){this._hooks=e,this._clock=t,this._token=0,this._raf=0,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase||"preparing"===this._state?.phase}get presented(){return this._state?.presented||null}start(e,t,i,n=220){this.cancel(!1);const r=++this._token,o=this._clock.now();if(this._state={token:r,phase:n<=0?"settling":"running",from:e,to:t,presented:n<=0?t:e,startedAt:o,duration:Math.max(0,n),targetMode:i},n<=0)return this._hooks.frame(this._state),this._hooks.settled(this._state),this._state=null,r;this._hooks.frame(this._state);const s=e=>{const t=this._state;if(!t||t.token!==r)return;const i=N_((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:function(e,t,i){const n=B_(i),r=H_(e.stageWidth,t.stageWidth,n),o=H_(e.stageHeight,t.stageHeight,n),s=Math.max(1e-9,e.viewport.pixelsPerUnit),a=Math.max(1e-9,t.viewport.pixelsPerUnit),l={centerX:H_(e.viewport.centerX,t.viewport.centerX,n),centerY:H_(e.viewport.centerY,t.viewport.centerY,n),pixelsPerUnit:Math.exp(H_(Math.log(s),Math.log(a),n))};return{presentedMode:n<.5?e.presentedMode:t.presentedMode,editorChromeHeight:H_(e.editorChromeHeight,t.editorChromeHeight,n),stageWidth:r,stageHeight:o,viewport:{...l,viewBox:W_(l,r,o)},stageColor:U_(e.stageColor,t.stageColor,n),paperColor:U_(e.paperColor,t.paperColor,n),sceneBrightness:H_(e.sceneBrightness,t.sceneBrightness,n),architectureOpacity:H_(e.architectureOpacity,t.architectureOpacity,n),backdropOpacity:H_(e.backdropOpacity,t.backdropOpacity,n),viewWeight:H_(e.viewWeight,t.viewWeight,n),editorWeight:H_(e.editorWeight,t.editorWeight,n),toolbarContentOpacity:H_(e.toolbarContentOpacity,t.toolbarContentOpacity,n)}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(s):(this._raf=0,this._hooks.settled(t),this._state?.token===r&&(this._state=null))};return this._raf=this._clock.requestFrame(s),r}cancel(e=!0){this._raf&&this._clock.cancelFrame(this._raf),this._raf=0;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=t.to,this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}}const G_={now:()=>"undefined"!=typeof performance?performance.now():Date.now(),requestFrame:e=>"function"==typeof requestAnimationFrame?requestAnimationFrame(e):null,cancelFrame:e=>{"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(e)}},K_=e=>Number.isFinite(e);function Y_(e){return!!e&&K_(e.zoom)&&e.zoom>0&&K_(e.viewBox.x)&&K_(e.viewBox.y)&&K_(e.viewBox.w)&&e.viewBox.w>0&&K_(e.viewBox.h)&&e.viewBox.h>0}function X_(e){return{zoom:e.zoom,viewBox:{...e.viewBox}}}function Z_(e,t){return Math.abs(e.zoom-t.zoom)<1e-9&&Math.abs(e.viewBox.x-t.viewBox.x)<1e-6&&Math.abs(e.viewBox.y-t.viewBox.y)<1e-6&&Math.abs(e.viewBox.w-t.viewBox.w)<1e-6&&Math.abs(e.viewBox.h-t.viewBox.h)<1e-6}const J_=(e,t,i)=>e+(t-e)*i,Q_=(e,t,i)=>Math.exp(J_(Math.log(e),Math.log(t),i));class eg{constructor(e,t=G_){this._hooks=e,this._clock=t,this._token=0,this._raf=null,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase}get presented(){return this._state?.presented||null}get target(){return this._state?.to||null}start(e,t,i,n){this.cancel(!1);const r=++this._token,o=X_(e),s=X_(t),a=n>0&&Y_(o)&&Y_(s),l=this._clock.now(),c={token:r,phase:a?"running":"settling",from:o,to:s,presented:X_(a?o:s),startedAt:l,duration:a?n:0,reason:i};if(this._state=c,this._hooks.frame(c),!a)return this._settle(r),r;const h=e=>{const t=this._state;if(!t||t.token!==r)return;const i=Math.max(0,Math.min(1,(e-t.startedAt)/t.duration));if(t.presented=i>=1?X_(t.to):function(e,t,i){if(!Y_(e))return X_(t);if(!Y_(t))return X_(e);const n=B_(i);if(n<=0)return X_(e);if(n>=1)return X_(t);const r=e.viewBox.x+e.viewBox.w/2,o=e.viewBox.y+e.viewBox.h/2,s=t.viewBox.x+t.viewBox.w/2,a=t.viewBox.y+t.viewBox.h/2,l=Q_(e.viewBox.w,t.viewBox.w,n),c=Q_(e.viewBox.h,t.viewBox.h,n),h=J_(r,s,n),d=J_(o,a,n);return{zoom:Q_(e.zoom,t.zoom,n),viewBox:{x:h-l/2,y:d-c/2,w:l,h:c}}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i>=1)return this._raf=null,void this._settle(r);this._raf=this._clock.requestFrame(h),null===this._raf&&this._settle(r)};return this._raf=this._clock.requestFrame(h),null===this._raf&&(c.phase="settling",c.presented=X_(s),this._hooks.frame(c),this._settle(r)),r}cancel(e=!0){null!==this._raf&&this._clock.cancelFrame(this._raf),this._raf=null;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=X_(t.to),this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}_settle(e){const t=this._state;t&&t.token===e&&(t.phase="settling",t.presented=X_(t.to),this._hooks.settled(t),this._state?.token===e&&(this._state=null))}}const tg=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])}),ig=64;function ng(e){return[e.rotDeg,e.tiltDeg,e.xyScale,e.zScale,e.origin[0],e.origin[1]].every(Number.isFinite)&&Math.abs(e.xyScale)>1e-12&&Math.abs(Math.cos(e.tiltDeg*Math.PI/180))>1e-12}function rg(e,t,i=tg){if(!(ng(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const n=i.rotDeg*Math.PI/180,r=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],s=e[1]-i.origin[1],a=(o*Math.cos(n)-s*Math.sin(n))*i.xyScale,l=(o*Math.sin(n)+s*Math.cos(n))*i.xyScale;return[i.origin[0]+a,i.origin[1]+l*Math.cos(r)-t*i.zScale*Math.sin(r)]}function og(e,t=tg){if(!ng(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,n=t.tiltDeg*Math.PI/180,r=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(n));return[t.origin[0]+r*Math.cos(i)+o*Math.sin(i),t.origin[1]-r*Math.sin(i)+o*Math.cos(i)]}function sg(e=tg){return`matrix(${function(e=tg){if(!ng(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,n=e.xyScale*Math.cos(t),r=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),s=e.xyScale*Math.cos(t)*Math.cos(i);return[n,o,r,s,e.origin[0]-n*e.origin[0]-r*e.origin[1],e.origin[1]-o*e.origin[0]-s*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function ag(e,t=tg){const{rect:i,wallHeight:n}=e,r=e.openingHeight??n,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(o)||n<0||r<0||o<0)throw new Error("invalid isometric frame");const s=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],a=Math.max(n,r),l=s.flatMap(e=>[rg(e,-o,t),rg(e,a,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),u=Math.min(...h);return{x:d,y:u,w:Math.max(...c)-d,h:Math.max(...h)-u}}const lg=[".dev",".vacpuck",".oplock",".op-hit",".opening",".rlgo","a","button","input","select","textarea",'[contenteditable]:not([contenteditable="false"])',"[data-room-fit-block]",'[role="link"]','[role="button"]'].join(","),cg=(e,t)=>{try{return"function"==typeof e?.matches&&e.matches(t)}catch{return!1}};function hg(e){for(const t of e){const e=t;if(cg(e,".roomlabel[data-id]")){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(cg(e,lg))return{kind:"interactive"};if(cg(e,'[data-hp="room"][data-id]')){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(cg(e,".stage"))return{kind:"background"}}return{kind:"outside"}}function dg(e){const t=hg(e);return"room"===t.kind?t.roomId:null}class ug{constructor(){this.pointer=null,this.sequence=null}clear(){this.pointer=null,this.sequence=null}clearOutside(e){let t={kind:"outside"};try{t=hg(e.composedPath())}catch{}"outside"===t.kind&&this.clear()}pointerDown(e,t,i){let n={kind:"outside"};try{n=hg(e.composedPath())}catch{}this.pointer=function(e){const t="mouse"===(i=e.pointerType)||"touch"===i||"pen"===i?i:null;var i;return t&&"view"===e.mode&&"background"===e.owner.kind&&!e.blocked&&e.isPrimary&&0===e.button&&Number.isFinite(e.x)&&Number.isFinite(e.y)?{pointerId:e.pointerId,spaceId:e.spaceId,modality:t,x:e.x,y:e.y}:null}({pointerId:e.pointerId,pointerType:e.pointerType,isPrimary:e.isPrimary,button:e.button,spaceId:t,mode:i?"view":"blocked",owner:n,blocked:!i,x:e.clientX,y:e.clientY}),this.pointer||(this.sequence=null)}pointerUp(e,t,i,n,r=Date.now()){let o={kind:"outside"};try{o=hg(e.composedPath())}catch{}const s=function(e,t,i){if(!t||"view"!==i.mode||"background"!==i.owner.kind||i.blocked||t.pointerId!==i.pointerId||t.spaceId!==i.spaceId||!Number.isFinite(i.x)||!Number.isFinite(i.y)||!Number.isFinite(i.now)||Math.abs(i.x-t.x)+Math.abs(i.y-t.y)>=8)return{sequence:null,trigger:!1};const n=e?i.now-e.at:Number.POSITIVE_INFINITY;return e&&e.spaceId===i.spaceId&&e.modality===t.modality&&n>=0&&n<=350?{sequence:null,trigger:!0}:{sequence:{at:i.now,spaceId:i.spaceId,modality:t.modality},trigger:!1}}(this.sequence,this.pointer,{pointerId:e.pointerId,spaceId:t,mode:i?"view":"blocked",owner:o,blocked:n||!i,x:e.clientX,y:e.clientY,now:r});return this.pointer=null,this.sequence=s.sequence,s.trigger}}const pg=e=>e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function mg(e){return!!e&&Number.isFinite(e.x)&&Number.isFinite(e.y)&&Number.isFinite(e.w)&&e.w>0&&Number.isFinite(e.h)&&e.h>0}function _g(e,t=0){if(!e.length||!Number.isFinite(t)||t<0||!e.every(pg))return null;let i=1/0,n=1/0,r=-1/0,o=-1/0;for(const t of e)i=Math.min(i,t[0]),n=Math.min(n,t[1]),r=Math.max(r,t[0]),o=Math.max(o,t[1]);const s=r-i,a=o-n;return s>0&&a>0?{x:i-t,y:n-t,w:s+2*t,h:a+2*t}:null}function gg(e,t){return`${e("editor.load_failed")} ${e(t.terminal?"editor.refresh_advice":"editor.retry_advice")}`}class fg extends Error{}class vg{constructor(e){this.options=e,this._state="idle",this._inFlight=null}get state(){return this._state}ensure(){return"ready"===this._state?Promise.resolve(!0):"failed"===this._state?Promise.resolve(!1):(this._inFlight||(this._setState("loading"),this._inFlight=this._loadWithRetry().finally(()=>{this._inFlight=null})),this._inFlight)}async _loadWithRetry(){let e=new Error("Editor runtime did not load"),t=!1;for(const i of[0,1])try{const e=await this.options.load(i);if(e.fingerprint!==this.options.expectedFingerprint)throw new fg(`Editor runtime fingerprint mismatch: expected ${this.options.expectedFingerprint}, got ${e.fingerprint}`);const t=e.create();return this.options.install(t),this._setState("ready"),!0}catch(i){e=i,i instanceof fg&&(t=!0)}return this._setState(t?"failed":"idle"),this.options.failed?.(e,{terminal:t}),!1}_setState(e){e!==this._state&&(this._state=e,this.options.stateChanged?.(e))}}const yg=(e,t)=>{if(!e.has(t))return{hit:!1};const i=e.get(t);return e.delete(t),e.set(t,i),{hit:!0,value:i}},bg=(e,t,i,n)=>{for(e.delete(t),e.set(t,i);e.size>n;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},wg=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),kg=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`,xg="houseplan_card_alpha_v1",Sg=Object.freeze([Object.freeze({id:"iso",issue:89,summary:"Volumetric plan renderer"})]);function $g(e){return/^[a-z][a-z0-9-]*$/.test(e.id)&&Number.isInteger(e.issue)&&e.issue>0&&!!e.summary.trim()}function Mg(e){const t=String(e||"").replace(/^#/,"");return new URLSearchParams(t)}function Cg(e){return Mg(e).get("space")||""}function Rg(e,t,i=Sg){const n=function(e){const t=new Set;for(const i of e){if(!$g(i)||t.has(i.id))return!1;t.add(i.id)}return!0}(i),r=function(e){let t,i=!1;const n=e=>{for(const n of e.getAll("hp_alpha"))i=!0,"1"===n?t=!0:"0"===n&&(t=!1)};return n(new URLSearchParams(String(e.search||"").replace(/^\?/,""))),n(Mg(e.hash)),{present:i,value:t}}(e),o=void 0!==r.value,s="1"===t,a=o?!0===r.value:!r.present&&s,l=n&&a;return{alpha:l,active:Object.freeze(l?i.map(e=>e.id).sort():[]),space:Cg(e.hash),persist:n&&o?l?"1":"0":void 0,knownUrlOperation:o}}let Tg={alpha:!1,active:Object.freeze([]),space:""},Dg=!1,zg="";const Ag=new Set;function Pg(){if("undefined"==typeof window)return Tg;const e=Rg(window.location,function(){try{return window.localStorage.getItem(xg)}catch{return null}}());void 0!==e.persist&&function(e){try{window.localStorage.setItem(xg,e)}catch{}}(e.persist),Tg={alpha:e.alpha,active:e.active,space:e.space},window.__hpAlpha=e.alpha,window.__hpLabs=e.active;for(const e of Ag)e(Tg);return Tg}function Fg(){Pg()}function Og(){if("undefined"==typeof window)return Tg;if(!Dg){Dg=!0,window.__hpLabsListenerCleanup?.(),window.addEventListener("hashchange",Fg),window.addEventListener("popstate",Fg);const e=()=>{window.removeEventListener("hashchange",Fg),window.removeEventListener("popstate",Fg),window.__hpLabsListenerCleanup===e&&delete window.__hpLabsListenerCleanup};window.__hpLabsListenerCleanup=e}return Pg()}const Ig=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Eg(e){let t=0;for(let i=0;iArray.isArray(e)&&Number.isFinite(e[0])&&Number.isFinite(e[1])).map(e=>[Number(e[0]),Number(e[1])]),i=t[t.length-1];return t.length>1&&t[0][0]===i[0]&&t[0][1]===i[1]&&t.pop(),t}(e);if(i.length<3)return[];const n=!t;return Eg(i)>0===n?i:[...i].reverse()}function Hg(e,t,i){return e.length?`M ${e.map(e=>Ig(rg(e,i,t))).join(" L ")} Z`:""}function Lg(e,t,i){const n=t[1]-e[1],r=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return n*Math.sin(o)+r*Math.cos(o)}function Bg(e,t,i=tg){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const n=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=Ng(e,t);if(!i.length)return i;let n=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(Eg(e))>=1e-9)).filter(e=>e.length&&Eg(e[0])>0);n.sort((e,t)=>{const i=e[0][0],n=t[0][0];return i[0]-n[0]||i[1]-n[1]||Math.abs(Eg(t[0]))-Math.abs(Eg(e[0]))});const r=[],o=[];let s=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:r.join(" "),sides:o,componentCount:n.length,edgeCount:s}}function qg(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const Wg=e=>e.every(Number.isFinite);function jg(e,t,i,n){const r=e[0]*i,o=e[1]*n,s=t*Math.PI/180;return[r*Math.cos(s)-o*Math.sin(s),r*Math.sin(s)+o*Math.cos(s)]}function Ug(e,t,i,n,r,o,s){const a=e.flipH?-1:1,l="gate"===e.type?1:e.flipV?-1:1,c=(e.type,e.flipV,e.angle,e.face,{ox:0,oy:0}),h=function(e,t){return[e[0]+t[0],e[1]+t[1]]}([e.x+c.ox,e.y+c.oy],jg(i,e.angle,a,l));return{leaf:t,hinge:h,closedVector:jg(n,e.angle,a,l),quarterVector:jg([-n[1],n[0]],e.angle,a,l),turnDeg:r,bottom:o,top:s}}const Vg=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Gg(e,t,i=tg){const n=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const r=function(e,t){const i=e.turnDeg*t*Math.PI/180;return[e.hinge[0]+e.closedVector[0]*Math.cos(i)+e.quarterVector[0]*Math.sin(i),e.hinge[1]+e.closedVector[1]*Math.cos(i)+e.quarterVector[1]*Math.sin(i)]}(t,n),o=rg(t.hinge,t.bottom,i),s=rg(r,t.bottom,i),a=rg(r,t.top,i),l=rg(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${Vg(o)} L ${Vg(s)} L ${Vg(a)} L ${Vg(l)} Z`,shadowD:`M ${Vg(rg(t.hinge,0,i))} L ${Vg(rg(r,0,i))}`,depth:Math.max(o[1],s[1])}})}const Kg={window:120,door:90,passage:90,gate:300};function Yg(e){return Kg[e]}function Xg(e,t){return{type:e,lengthCm:Yg(e),flipH:!1,flipV:!1,revision:t}}function Zg(e,t){const i=Math.max(0,e.renderedLength)/2,n=Math.max(0,e.target.physicalHalfWidth),r=n+.18*Math.max(0,t),o=e=>({x1:e,y1:-r,x2:e,y2:r});return{rect:{x:-i,y:n?-n:0,width:2*i,height:2*n},boundaries:[o(-i),o(i)]}}function Jg(e,t){const i=[e[0],e[1]],n=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,n)<=0?{a:i,b:n}:{a:n,b:i}}function Qg(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const n=Jg(e.a,e.b);if(Math.hypot(n.b[0]-n.a[0],n.b[1]-n.a[1])<=1e-9)return;const r=function(e,t){const i=e=>(Math.abs(e)<=5e-10?0:Math.round(1e6*e)/1e6).toFixed(6);return`${i(e[0])},${i(e[1])}>${i(t[0])},${i(t[1])}`}(n.a,n.b),o=t.get(r);if(o)return o.physicalHalfWidth=Math.max(o.physicalHalfWidth,e.half||0),o.sourceOrder=Math.min(o.sourceOrder,i),void(e.partitionHost&&(o.partitionHost&&o.partitionHost.id!==e.partitionHost.id?o.ambiguousPartitionHost=!0:o.partitionHost=e.partitionHost));t.set(r,{segmentKey:r,a:n.a,b:n.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function ef(e,t){if(Math.abs(e.distance-t.distance)>1e-9)return e.distance-t.distance;if(Math.abs(e.perpendicular-t.perpendicular)>1e-9)return e.perpendicular-t.perpendicular;return(e.target.segmentKeyt.target.segmentKey?1:0)||e.target.sourceOrder-t.target.sourceOrder}function tf(e){const t=Qg(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,a=(e[0]-t.a[0])*o+(e[1]-t.a[1])*s,l=Math.max(0,Math.min(r,a)),c=t.a[0]+l*o,h=t.a[1]+l*s;return{along:l,length:r,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*s-(e[1]-t.a[1])*o)}}(e.pointer,t);return{target:t,...i,envelope:Math.max(e.baseTolerance,t.physicalHalfWidth+e.bodyPointerPadding)}}).filter(e=>e.distance<=e.envelope+1e-9).filter(t=>!function(e,t,i,n,r){const o=t.b[0]-t.a[0],s=t.b[1]-t.a[1],a=Math.hypot(o,s);if(!(a>1e-9))return!1;const l=o/a,c=s/a;for(const o of i){if(!o.open&&o.kind)continue;const i=Jg(o.a,o.b),s=i.b[0]-i.a[0],a=i.b[1]-i.a[1],h=Math.hypot(s,a);if(!(h>1e-9))continue;const d=s/h,u=a/h;if(Math.abs(l*u-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>r)continue;const p=(e[0]-i.a[0])*d+(e[1]-i.a[1])*u,m=1e-9;if(!(p<=m||p>=h-m)&&Math.abs((e[0]-i.a[0])*u-(e[1]-i.a[1])*d)<=n+1e-9)return!0}return!1}(e.pointer,t.target,e.intervals,t.envelope,Math.max(1e-9,Math.min(e.baseTolerance,.04*e.gridStep)))),i=t.filter(t=>!!t.target.partitionHost&&e.renderedLength+2*t.target.physicalHalfWidth>t.length+1e-9).sort(ef),n=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(ef);let r=n[0];if(!r)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=n.filter(e=>Math.abs(e.distance-r.distance)<=1e-9&&Math.abs(e.perpendicular-r.perpendicular)<=1e-9),s=o.filter(e=>e.target.partitionHost);if(s.length){if(1!==new Set(s.map(e=>e.target.partitionHost.id)).size||s.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=s[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],n=Math.hypot(t,i),a=t/n,l=i/n;if(!o.every(t=>{const i=t.target.b[0]-t.target.a[0],n=t.target.b[1]-t.target.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,c=Math.abs(a*s-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*a);return c&&h<=1e-9}))return{candidate:null,jambBlockedTarget:null};r=e}if(r.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:a,length:l}=r,c=a.b[0]-a.a[0],h=a.b[1]-a.a[1],d=c/l,u=h/l,p=a.partitionHost?a.physicalHalfWidth:0,m=Math.min(Math.max(0,e.renderedLength)/2+p,l/2);let _=r.along;const g=Math.max(e.gridStep,1e-9),f=l/2;_=Math.abs(_-f)<=g/2?f:Math.round(_/g)*g,_=Math.max(m,Math.min(l-m,_));const v=a.a[0]+d*_,y=a.a[1]+u*_,b=Math.max(0,e.renderedLength)/2,w=_-b,k=_+b,x=Math.max(0,w),S=Math.max(0,l-k),$=[a.a[0]+d*(w-x/2),a.a[1]+u*(w-x/2)],M=[a.a[0]+d*(k+S/2),a.a[1]+u*(k+S/2)];let C=180*Math.atan2(h,c)/Math.PI;C>=90?C-=180:C<-90&&(C+=180);const R=Math.abs(_-f)<=1e-9;return{candidate:{presetRevision:e.preset.revision,geometryRevision:e.geometryRevision,pointer:[e.pointer[0],e.pointer[1]],type:e.preset.type,lengthCm:e.preset.lengthCm,flipH:e.preset.flipH,flipV:e.preset.flipV,x:v,y:y,angle:C,renderedLength:e.renderedLength,target:a,...a.partitionHost?{host:{...a.partitionHost,t:l>0?_/l:0}}:{},measure:{labels:[{distance:x,midpoint:$},{distance:S,midpoint:M}],guide:R?{x:v,y:y,angle:C}:null}},jambBlockedTarget:null}}function nf(e,t,i,n,r=1e-6){return e.presetRevision===i&&e.geometryRevision===n&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=r}function rf(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}let of=0;const sf="1.72.0-beta.2",af="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7",lf=1500,cf=new Map;let hf=0,df=1e4;const uf="houseplan_card_layout_v1",pf="houseplan_card_cfg_v1",mf="houseplan_card_zoom_v1",_f="houseplan_card_nav_v1",gf="houseplan_card_kiosk_v1",ff="houseplan_card_view_v1",vf="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",yf=1e3,bf=(e,t)=>{const i=Math.min(e.x,t.x),n=Math.min(e.y,t.y);return{x:i,y:n,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-n}},wf=(e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)},kf=e=>{history.pushState(null,"",e),wf(window,"location-changed",{replace:!1})},xf=(e,t)=>{let i,n=null;const r=(...r)=>{clearTimeout(i),n=r,i=window.setTimeout(()=>{i=void 0;const t=n;n=null,t&&e(...t)},t)};return r.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=n;n=null,t&&e(...t)},r.cancel=()=>{clearTimeout(i),i=void 0,n=null},r.pending=()=>void 0!==i,r},Sf=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}},$f=["edges","corners"];class Mf extends he{constructor(){super(...arguments),this._editorRuntime=null,this._liveRt=null,this._onboardingRuntime=null,this._editorRuntimeLoadingVisible=!1,this._backdropGuard=null,this._editorModeRequest=0,this._warmModeRequest=0,this._editorRuntimeLoader=new vg({expectedFingerprint:af,load:async e=>{const t=0===e?await import("./houseplan-editor-runtime-C5_zNXOu.js"):await import((()=>{const e=new URL("./houseplan-editor-runtime-C5_zNXOu.js",import.meta.url);return e.searchParams.set("hp_retry",`${sf}-${++of}`),e.href})());return{fingerprint:t.EDITOR_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanEditorRuntime(this)}},install:e=>{this._editorRuntime=e},stateChanged:e=>this._editorRuntimeStateChanged(e),failed:(e,t)=>{console.error("[houseplan] unable to load editor runtime",e),this._showToast(gg(e=>this._t(e),t))}}),this._onboardingRuntimeLoader=new vg({expectedFingerprint:af,load:async e=>{const t=0===e?await import("./houseplan-onboarding-runtime-C0Zl7v7b.js"):await import((()=>{const e=new URL("./houseplan-onboarding-runtime-C0Zl7v7b.js",import.meta.url);return e.searchParams.set("hp_retry",`${sf}-${++of}`),e.href})());return{fingerprint:t.ONBOARDING_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanOnboardingRuntime(this)}},install:e=>{this._onboardingRuntime=e},failed:(e,t)=>{console.error("[houseplan] unable to load onboarding runtime",e),this._showToast(gg(e=>this._t(e),t))}}),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._cfgContentFingerprint="",this._unsubCfg=null,this._unsubLayout=null,this._unsubVirtual=null,this._liveSyncAttempt=null,this._liveSyncGeneration=0,this._liveSyncConnection=null,this._layoutRev=0,this._layoutContentFingerprint="",this._virtualLights=Yc(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._areaRelocationIds=new Set,this._areaSnapshotCleanupCandidates=new Map,this._areaRelocationSyncKey="",this._areaRelocationWrite=Promise.resolve(),this._tip=null,this._hoverRoom=null,this._pointerModality=new __(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...rr},this._decorStyleSeeded=!1,this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._decorImagePalette=null,this._decorAssetCatalog=[],this._decorAssets=new Map,this._decorAssetBusy=!1,this._furnCategory=null,this._furnPreviewInput=null,this._furnTouchPending=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this._cancelDevicePressFeedback(),e.matches&&this._cancelCameraTransition(!0),e.matches&&this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:e.matches&&this._modeTransition.active&&this._cancelModeTransition(!0),this.requestUpdate()},this._editorChromeMode="plan",this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest=0,this._modeTransitionTargetZoom=1,this._modeTransitionEditorCamera=null,this._modeTransition=new V_({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._cameraTransitionFit=null,this._cameraTransition=new eg({frame:e=>this._applyCameraTransitionFrame(e),settled:e=>this._settleCameraTransition(e)}),this._editorSecondaryCopy={groupActive:(e,t)=>this._t("editor.group_active",{group:e,item:t}),openGroup:e=>this._t("editor.open_group",{group:e}),disabledAction:(e,t)=>this._t("editor.disabled_action",{action:e,reason:t})},this._tool="draw",this._geometryHistory=new R_(50),this._devicePositionHistory=new R_(50),this._devicePositionBusy=!1,this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._partitionDeleteDialog=null,this._roomDeleteDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCacheValue=null,this._wallUnionPool=new Map,this._isoGeometryCache=new Map,this._isoFallback=new Set,this._openingTunnelCache=null,this._openingWallIndexCache=new Map,this._openingPlacementIntervalsCache=null,this._openingDimensionContextCache=null,this._planSnapGeometryCache=null,this._planStructuralGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._physicalBodiesCache=null,this._lightPhysicalBodiesCache=null,this._cleanFloorCache=new Map,this._innerContourCache=new Map,this._glowRuntimeState=Tm(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._roomFocus=null,this._roomPointer=null,this._doubleFit=new ug,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._viewportGestureDirty=!1,this._touchContacts=new Map,this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._connectedPath="",this._routeDepartureHandled=!1,this._onLocationChanged=()=>{this._connectedPath&&location.pathname!==this._connectedPath?this._leaveCardRoute():location.pathname===this._connectedPath&&(this._routeDepartureHandled=!1)},this._touchGestureGuard={capture:!0,handleEvent:e=>this._guardTouchGesture(e)},this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootSettling=!1,this._bootSettleRaf=0,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._dangerConfirm=null,this._dangerConfirmController=new ge(e=>{this._dangerConfirm=e}),this._confirmDanger=e=>this._config&&this.hass?"warm"===this._syncDangerConfirmLocaleGate()||this._dangerConfirmMissingSpace()?Promise.resolve(!1):this._dangerConfirmController.confirm(e):Promise.resolve(!1),this._cancelDangerConfirm=()=>{this._dangerConfirmController.cancel()},this._onDangerConfirmDecision=e=>{this._dangerConfirmController.resolve(e.detail.token,e.detail.accepted)},this._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._haSupportApi=null,this._haDecorAssetsApi=null,this._decorAssetSyncToken=0,this._alignDialog=null,this._settingsDialog=null,this._supportDialog=null,this._backupExportDialog=null,this._backupImportDialog=null,this._sunRaysCache=null,this._dayCycleTimer=0,this._dayCycleClockKey="",this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._nativeMoreInfoEntity=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++hf,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={alpha:!1,active:Object.freeze([]),space:""},this._viewPreference={},this._renderProjection="flat",this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotLayout=null,this._capturedSnapshotActivity="",this._capturedSnapshotConfigEpoch=-1,this._capturedSnapshotVirtual="",this._lastValidStageSize=null,this._pendingRefitSize=null,this._refitRaf=0,this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"===e.kind)return this._clearRoomFocus(!0),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._cancelCameraTransition(!0),void(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0));if(this._vacJumpOnce=!0,!e.long){const e=Date.now();let t=!1;for(const i of this._activityRt.values())!i.flashKind||(i.expiresAt||i.flashTs+h_)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&this.requestUpdate())}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._resumeSettling=!0,this._loading?this.requestUpdate():this._loadFromServer()},this._resumeSettling=!1,this._viewportInvalidAt=0,this._vacFit=null,this._vacAllCamerasFor=null,this._vacAllCameraCache=null,this._vacCalConfirm=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._tabDrag=null,this._tabDragRelease=null,this._tabSuppressClick=!1,this._tabOrderWarned=!1,this._onLabsSnapshot=e=>{const t=this._effectiveProjection();this._labs=e;const i=this._effectiveProjection();this._convertProjectionView(t,i),this.requestUpdate()},this._onHashChange=()=>{if(this._hasFixedFloor)return;const e=this._hashSpace();if(e&&this._model.find(t=>t.id===e)&&e!==this._space){if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._selId=null,this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate()}},this._drag=null,this._deviceDrag=null,this._rlResize=null,this._holdFired=!1,this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._loading||this._loadFromServer()},this._cfgEpoch=0,this._cfgEpochPreservedConfig=null,this._terminalFrame=0,this._modelCache=null,this._emptySpaceStateActive=!1,this._decorSnapCache=null,this._markerPreviewMemo=null,this._markerPreviewDevicesMemo=null,this._showHidden=!1,this._connHooked=null,this._connectionWasLost=!1,this._haRegistryConnection=null,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._renderLife=new z_,this._liveEditorPaintCount=0,this._onHaRegistryUpdate=()=>{const e=mh(this.hass);e.revision===this._haRegistryRev&&this._devices.length||(this._haRegistryRev=e.revision,this._renderLife.invalidate(),this._planHassMemo=null,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())},this._onConnReady=()=>{if(this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,ph(this.hass),this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(this._onConnReady,400));!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._loading||this._loadFromServer()},this._onConnLost=()=>{this._booting&&!this._continuity.hasCompleteFrame||(this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost())},this._signer=new Ll(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=xf(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=Qr(this._layout[t]);e&&(this._layout={...this._layout,[t]:e},this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(e=>this._noteLayoutRev(e)).catch(e=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)}))}this._cacheSnapshot()}else this._persistLocalLayout()},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>this._editorRuntimeOrThrow()._undoGeometry(),this._redoGeometry=()=>this._editorRuntimeOrThrow()._redoGeometry(),this._saveConfigDebounced=xf(()=>{this._serverCfg&&this._writeConfig().catch(e=>{const t=!0===e?.physicalGeometryRolledBack;"geometry-unsafe"!==e?.code&&("wall_model_client_outdated"===e?.code?this._showToast(this._t("toast.wall_model_client_outdated")):"conflict"===e?.code?(this._showToast(this._t("toast.conflict")),t||(this._cancelPath(),this._reloadConfigOnly(!0))):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)})),t&&this._reloadRejectedPhysicalWrite())})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>this._editorRuntimeOrThrow()._savePhysicalDialog(),this._deletePhysicalSelection=()=>this._editorRuntimeOrThrow()._deletePhysicalSelection(),this._confirmPartitionDelete=()=>this._editorRuntimeOrThrow()._confirmPartitionDelete(),this._deleteDraftWhole=()=>this._editorRuntimeOrThrow()._deleteDraftWhole(),this._deleteDraftSegment=()=>this._editorRuntimeOrThrow()._deleteDraftSegment(),this._confirmRoomDelete=e=>this._editorRuntimeOrThrow()._confirmRoomDelete(e),this._rebindPartitionOpening=()=>this._editorRuntimeOrThrow()._rebindPartitionOpening(),this._keepClosedAsPartitions=()=>this._editorRuntimeOrThrow()._keepClosedAsPartitions(),this._toggleServerPlans=async()=>this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._toggleServerPlans():this._editorRuntimeOrThrow()._toggleServerPlans(),this._aspectJob=null,this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=qn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(this._editorRuntime)return this._editorRuntime._openSettingsDialog();this._ensureEditorRuntime().then(e=>{e&&this._openSettingsDialog()})},this._openSupportDialog=()=>{this._editorRuntime?this._editorRuntime._openSupportDialog():this._ensureEditorRuntime().then(e=>{e&&this._openSupportDialog()})},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._editorRuntimeOrThrow()._openAlignDialog(),this._toggleOptimizeLivePositions=()=>this._editorRuntimeOrThrow()._toggleOptimizeLivePositions(),this._optimizeUndoBusy=!1,this._openBackupExport=()=>this._editorRuntimeOrThrow()._openBackupExport(),this._openRulesDialog=()=>this._editorRuntimeOrThrow()._openRulesDialog(),this._climateCache=null,this._gearPtCache=new WeakMap}requestUpdate(e,t,i){if("hass"===e&&this.hass&&this._liveRt){const e=this._visibleDeviceSnapshot||this._candidateDeviceSnapshot;if(!this._liveRt.hass(t,this.hass,e?{entityIds:e.entityIds}:null,()=>A_(this)))return}void 0!==e&&(this._terminalFrame=0),this._editorRuntime?._routeLiveEditorUpdate(e,t)||(this._liveRt?.clear(),super.requestUpdate(e,t,i))}_editorRuntimeOrThrow(){if(!this._editorRuntime)throw new Error("Houseplan editor runtime is not loaded");return this._editorRuntime}async _ensureEditorRuntime(){return this._editorRuntimeLoader.ensure()}async _ensureOnboardingRuntime(){return this._onboardingRuntimeLoader.ensure()}_editorRuntimeStateChanged(e){clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,"loading"!==e?(this._editorRuntimeLoadingVisible=!1,this.requestUpdate()):this._editorRuntimeLoadingTimer=window.setTimeout(()=>{this._editorRuntimeLoadingVisible=!0,this.requestUpdate()},150)}async _requestMode(e,t=!0,i=!1){const n=++this._editorModeRequest;if(i&&(this._warmModeRequest=n,this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._pendingRefitSize=null),"view"===e||await this._ensureEditorRuntime()){if(n===this._editorModeRequest&&this.isConnected)return i?(this._adoptMode(e),this._warmRevivePending&&(clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmReviveDialog()),this.requestUpdate(),void this.updateComplete.then(()=>requestAnimationFrame(()=>requestAnimationFrame(()=>{if(this._warmModeRequest!==n||n!==this._editorModeRequest)return;const e=this._stageEl;this._lastValidStageSize=e&&e.clientWidth>0&&e.clientHeight>0?[e.clientWidth,e.clientHeight]:null,this._pendingRefitSize=null,this._warmModeRequest=0})))):void this._setMode(e,t);this._warmModeRequest===n&&(this._warmModeRequest=0)}else this._warmModeRequest===n&&(this._warmModeRequest=0)}_seedDecorStyle(e){if(this._decorStyleSeeded||!e)return;this._decorStyleSeeded=!0;const t=e.settings?.decor_default_style;t&&(this._decorStyle=function(e,t){if(!e||"object"!=typeof e)return{...t};const i=e,n="string"==typeof i.color&&i.color?i.color:t.color,r="string"==typeof i.fill_color&&i.fill_color?i.fill_color:t.fillColor,o=Number(i.width_cm);return{color:n,opacity:or(i.opacity,t.opacity),widthCm:Number.isFinite(o)&&o>0?Math.max(.1,Math.min(100,o)):t.widthCm,fill:"boolean"==typeof i.fill?i.fill:t.fill,fillColor:r,fillOpacity:or(i.fill_opacity,t.fillOpacity)}}(t,rr))}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}get _modeTransitionBusy(){return this._modeTransitionPreparing||this._modeTransition.active}get _doubleFitEnabled(){return!("view"!==this._mode||this._vacFit||this._modeTransitionBusy||this._touchSequenceMultitouch||"steady"!==this._continuity.state||this._continuity.overlayBlocksInteraction)}_cameraState(){const e=this._viewOr(this._baseVb());return{zoom:this._zoom,viewBox:{...e}}}_normalizeCameraState(e){const t=this._cameraTransitionFit||vi(this._baseVb(),this._stageAspect());return{zoom:Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,e.zoom)),viewBox:this._clampView({...e.viewBox},t)}}_applyCameraTransitionFrame(e){const t=this._normalizeCameraState(e.presented);e.presented=t,this._zoom=t.zoom,this._view={...t.viewBox},this._liveVp(!0)}_settleCameraTransition(e){const t=this._normalizeCameraState(e.to);this._zoom=t.zoom,this._view={...t.viewBox},this._cameraTransitionFit=null,"room"!==e.reason&&this._saveZoom(),this.requestUpdate()}_cancelCameraTransition(e=!1,t=!1){const i=this._cameraTransition.state?.reason,n=t&&this._cameraTransition.active?this._cameraTransition.presented?.zoom:void 0;this._cameraTransition.cancel(e),this._cameraTransitionFit=null,void 0!==n&&"room"!==i&&this._saveZoom()}_prepareCameraCommand(){this._modeTransitionBusy&&this._cancelModeTransition(!0),"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1))}_startCameraTransition(e,t,i,n){const r=this._cameraState(),o=this._cameraTransition.target;return(!o||!Z_(o,e))&&(Z_(r,e)?(this._cancelCameraTransition(!1),!1):(this._cameraTransitionFit={...t},this._cameraTransition.start(r,e,i,this._reducedMotion?0:n),!0))}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const n=document.createElement("span");n.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(n);const r=getComputedStyle(n).color||t;return n.remove(),r}_currentModeVisual(e=this._mode){const t=this._modeTransition.presented||this._modeTransitionVisual;if(t)return{...t,viewport:{...t.viewport,viewBox:{...t.viewport.viewBox}}};const i=this._stageEl;if(!i||i.clientWidth<=0||i.clientHeight<=0)return null;const n=this.renderRoot.querySelector(".editorchrome"),r=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),s=this.renderRoot.querySelector(".hp-backdrop"),a=this.renderRoot.querySelector(".zoomwrap"),l=a?getComputedStyle(a).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:n?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:q_(r,i.clientWidth),stageColor:getComputedStyle(i).backgroundColor||"rgb(255, 255, 255)",paperColor:o?getComputedStyle(o).fill:"rgb(255, 255, 255)",sceneBrightness:Number.isFinite(c)?c:1,architectureOpacity:"decor"===e?.35:1,backdropOpacity:s&&Number(getComputedStyle(s).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,n,r){const o=vi(this._baseVb(),n/Math.max(1,r)),s=Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,e)),a=o.w/s,l=o.h/s,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-a/2,y:h-l/2,w:a,h:l},o)}_targetStageColor(e){return"view"!==e?"rgb(255, 255, 255)":this._cssColor(this._stageBg(this._spaceDisplayForRender()),this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"))}_targetPaperColor(e){return"view"===e&&this._spaceModel()?.bg?this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"):"rgb(255, 255, 255)"}_targetBrightness(e){return 1}_applyModeTransitionFrame(e){e.targetMode===this._mode&&(this._modeTransitionVisual=e.presented,this._view={...e.presented.viewport.viewBox},this.requestUpdate())}_settleModeTransition(e){if(e.targetMode!==this._mode)return;const t=this._modeTransitionRequest;this._view={...e.to.viewport.viewBox},this._zoom=this._modeTransitionTargetZoom,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._lastValidStageSize=[e.to.stageWidth,e.to.stageHeight],"view"===this._mode&&(this._saveZoom(),this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate(),this.updateComplete.then(()=>{if(!this.isConnected||t!==this._modeTransitionRequest||this._modeTransitionBusy||e.targetMode!==this._mode)return;const i=this._stageEl;if(i&&i.clientWidth>0&&i.clientHeight>0&&(Math.abs(i.clientWidth-e.to.stageWidth)>.5||Math.abs(i.clientHeight-e.to.stageHeight)>.5)){const e=this._view;this._lastValidStageSize=[i.clientWidth,i.clientHeight],this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}const n=this.renderRoot.activeElement;(!n||!n.isConnected||!!n.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){this._cancelCameraTransition(!1);const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){e!==this._mode&&this._clearRoomFocus(!0),this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,n){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const r=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,n,r,o),this._lastValidStageSize=[r,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,n,r,o){this.updateComplete.then(()=>{if(!this.isConnected||e!==this._modeTransitionRequest||this._mode!==i)return void(e===this._modeTransitionRequest&&(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this.requestUpdate()));const s=this.renderRoot.querySelector(".editorchrome"),a=s?.querySelector(".editorchrome-inner"),l="view"===i?0:a?.scrollHeight||a?.getBoundingClientRect().height||0,c=Math.max(1,t.stageHeight+t.editorChromeHeight),h=Math.max(1,c-l),d=this._stageEl?.clientWidth||t.stageWidth;if(d<=0||h<=0)return this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._applyView(n,r,o),void this.requestUpdate();const u=this._viewForModeTarget(n,r,o,d,h),p={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:q_(u,d),stageColor:this._targetStageColor(i),paperColor:this._targetPaperColor(i),sceneBrightness:this._targetBrightness(i),architectureOpacity:"decor"===i?.35:1,backdropOpacity:"decor"===i&&"backdrop"!==this._decorTool?.5:1,viewWeight:"view"===i?1:0,editorWeight:"view"===i?0:1,toolbarContentOpacity:"view"===i?0:1};this._modeTransitionPreparing=!1;const m=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,p,i,this._reducedMotion||m?0:220)})}get _hasFixedFloor(){return!!this._config&&Object.prototype.hasOwnProperty.call(this._config,"floor")}_fixedFloorState(e=this._model,t=this._loadOk){const i=this._config?.floor,n=function(e){if(!e.hasFloor)return{kind:"absent"};const t=e.floor;if("string"==typeof t)return t.length?e.spaceIds.includes(t)?{kind:"valid",id:t,source:"id"}:{kind:"invalid",reason:"unknown-id",value:t}:{kind:"invalid",reason:"empty-id",value:t};if("number"==typeof t){if(!Number.isFinite(t))return{kind:"invalid",reason:"non-finite-index",value:t};if(!Number.isInteger(t))return{kind:"invalid",reason:"fractional-index",value:t};if(t<0)return{kind:"invalid",reason:"negative-index",value:t};const i=e.spaceIds[t];return void 0===i?{kind:"invalid",reason:"out-of-range-index",value:t}:{kind:"valid",id:i,source:"index"}}return{kind:"invalid",reason:"invalid-type",value:t}}({spaceIds:e.map(e=>e.id),hasFloor:this._hasFixedFloor,floor:i});return!this._hasFixedFloor||t?n:"number"==typeof i?"valid"===n.kind||"invalid"===n.kind&&"out-of-range-index"===n.reason?{kind:"pending",value:i}:n:"valid"===n.kind||"invalid"===n.kind&&"unknown-id"!==n.reason?n:{kind:"pending",value:i}}_canCommitSpace(e,t=!1){if(t||!this._hasFixedFloor)return!0;const i=this._fixedFloorState();return"valid"===i.kind&&i.id===e}_commitSpace(e,t=!1){return!!this._canCommitSpace(e,t)&&(e!==this._space&&(this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._cancelCameraTransition(!1),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._editorRuntime?._clearFurniturePreview()),this._space=e,!0)}_slideTo(e,t){if(e===this._space)return!0;if(!this._canCommitSpace(e))return!1;if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return!1;this._cancelModeTransition(!0);const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;return this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._editorSecondary?.closeForNavigation(),this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=t,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate()),!0}get _canReorderTabs(){return rf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){rf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(Sf(e),this._tabDragRelease=e=>this._tabPointerUp(e),window.addEventListener("pointerup",this._tabDragRelease),window.addEventListener("pointercancel",this._tabDragRelease),this._tabDrag={id:t,pointerId:e.pointerId,x:e.clientX,y:e.clientY,moved:!1,targetId:null,placement:null})}_tabDropTargetAt(e,t,i){const n=this._model.map(e=>e.id),r=n.indexOf(i);if(r<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const s of o){const o=s.dataset.id||"";if(!o||o===i)continue;const a=s.getBoundingClientRect();if(ea.right||ta.bottom)continue;const l=n.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,n;const r=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===r?.targetId&&t.placement===r?.placement||(this._tabDrag={...t,moved:!0,targetId:r?.targetId||null,placement:r?.placement||null})}_tabPointerUp(e){const t=this._tabDrag;if(t&&t.pointerId!==e.pointerId)return;const i="pointerup"===e.type&&t?.moved?this._tabDropTargetAt(e.clientX,e.clientY,t.id):null;"pointerup"===e.type&&t?.moved&&this._suppressNextTabClick(),this._endTabDrag(),t?.moved&&i&&this._commitTabOrder(t.id,i.targetId)}_endTabDrag(){this._tabDrag=null,this._tabDragRelease&&(window.removeEventListener("pointerup",this._tabDragRelease),window.removeEventListener("pointercancel",this._tabDragRelease),this._tabDragRelease=null)}_tabClick(e){if(this._tabSuppressClick)return this._tabSuppressClick=!1,clearTimeout(this._tabSuppressClickTimer),void(this._tabSuppressClickTimer=void 0);this._pickSpace(e)}_suppressNextTabClick(){this._tabSuppressClick=!0,clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=window.setTimeout(()=>{this._tabSuppressClick=!1,this._tabSuppressClickTimer=void 0},0)}_commitTabOrder(e,t){const i=this._serverCfg;if(!i||!this._canReorderTabs)return;const n=this._model.map(e=>e.id),r=function(e,t,i){const n=e.indexOf(t),r=e.indexOf(i);if(n<0||r<0||n===r)return e;const o=e.slice();return o.splice(n,1),o.splice(r,0,t),o}(n,e,t);if(r===n)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),s=function(e,t,i,n=()=>""){if(!i)return[];const r=[];for(const o of e){if(!o||!0===o.removed)continue;const e="string"==typeof o.id?o.id:"";if(!e)continue;if("string"==typeof o.space&&o.space)continue;const s=("string"==typeof o.area?o.area:"")||n(e)||"";s&&t[s]||r.push({id:e,space:i})}return r}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),n[0]||"",e=>o.get(e)||"");if(s.length){const e=new Map(s.map(e=>[e.id,e.space]));for(const t of i.markers||[]){const i=e.get(String(t.id));i&&(t.space=i)}}i.spaces=function(e,t){const i=new Map(t.map((e,t)=>[e,t]));return e.map((e,t)=>({space:e,index:t})).sort((e,t)=>(i.get(String(e.space?.id))??Number.MAX_SAFE_INTEGER)-(i.get(String(t.space?.id))??Number.MAX_SAFE_INTEGER)||e.index-t.index).map(e=>e.space)}(i.spaces||[],r),this._saveConfig(),this._tabOrderWarned||(this._tabOrderWarned=!0,this._showToast(this._t("toast.space_order_changed")))}_pickSpace(e){if(this._endTabDrag(),e===this._space)return;const t=this._model.map(e=>e.id),i=t.indexOf(this._space),n=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&n0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const e=this._model.map(e=>e.id),t=e.indexOf(this._space);this._slideTo(e[(t+1)%e.length],"left"),this._showKioskDots()}}_syncCycleTimer(){clearInterval(this._cycleTimer),this._cycleTimer=void 0,this.isConnected&&!this._hasFixedFloor&&this._config?.kiosk&&Number(this._config.cycle)>0&&(this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle)))}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}get _wallUnionCache(){return this._wallUnionCacheValue}set _wallUnionCache(e){this._wallUnionCacheValue=e,null===e&&this._wallUnionPool.clear()}get _glowClipCache(){return this._glowRuntimeState.clipCache}get _glowGeometryWarnings(){return this._glowRuntimeState.geometryWarnings}get _glowFeatherUnits(){return this._glowRuntimeState.featherUnits}set _glowFeatherUnits(e){this._glowRuntimeState.featherUnits=e}get _glowRenderedSources(){return this._glowRuntimeState.renderedSources}get _glowLastAppearance(){return this._glowRuntimeState.lastAppearance}get _glowFeatherSuspendUntil(){return this._glowRuntimeState.featherSuspendUntil}set _glowFeatherSuspendUntil(e){this._glowRuntimeState.featherSuspendUntil=e}get _glowFeatherResumeTimer(){return this._glowRuntimeState.featherResumeTimer}set _glowFeatherResumeTimer(e){this._glowRuntimeState.featherResumeTimer=e}get _glowSourceSeq(){return this._glowRuntimeState.sourceSeq}set _glowSourceSeq(e){this._glowRuntimeState.sourceSeq=e}_syncDangerConfirmLocaleGate(){return this._config&&this.hass?uu(this,fu,xu(this.hass,this._config.language)):"ready"}_dangerConfirmMissingSpace(){const e=this._model;if(!e.length)return!1;const t=this._fixedFloorState(e);return"pending"!==t.kind&&"invalid"!==t.kind&&!this._spaceModel()}_newContinuityController(){return new Il(()=>{this._resumeSettling="steady"!==this._continuity.state,this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}get _labsIso(){return this._labs.active.includes("iso")}get _desiredProjection(){return"view"===this._mode&&this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat"}_saveViewPreference(){try{localStorage.setItem(ff,JSON.stringify(this._viewPreference))}catch{}}_logicalViewCenter(e){const t=this._view;if(!t)return null;const i=[t.x+t.w/2,t.y+t.h/2],n="iso"===e?og(i):i;return{x:n[0],y:n[1]}}_convertProjectionView(e,t){if(e===t)return;this._clearRoomFocus(!0);const i=this._logicalViewCenter(e);this._view=null;const n=i?"iso"===t?rg([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,n?.[0],n?.[1]),this._warmPatch({vp:this._warmViewportState()}),this.requestUpdate()}_setProjection(e){if(!this._labsIso||"view"!==this._mode)return;const t=this._effectiveProjection();if(this._viewPreference={...this._viewPreference,[this._space]:e},"iso"===e){const e=this._isoSceneKey();e&&this._isoFallback.delete(e)}this._saveViewPreference();const i=this._effectiveProjection();this._convertProjectionView(t,i)}_hashSpace(){return Cg(window.location.hash||"")}connectedCallback(){this._connectedPath=location.pathname,this._routeDepartureHandled=!1,window.addEventListener("location-changed",this._onLocationChanged),window.addEventListener("popstate",this._onLocationChanged),this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1);const e=b_(this.ownerDocument);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=Fl(this.ownerDocument,this._pageVisibility),this._languageFailureUnsub?.(),this._languageFailureUnsub=(t=()=>{this._showToast(this._t("toast.locale_load_failed"))},gu.add(t),()=>gu.delete(t)),super.connectedCallback(),this._ensureLiveRuntime().catch(()=>this.requestUpdate()),this._pointerModality.connect(this.ownerDocument.defaultView);const i=this.ownerDocument.defaultView?.MutationObserver;i&&(this._pointerHoverObserver=new i(e=>{for(const t of e)for(const e of t.addedNodes)this._syncPointerHoverSubtree(e)}),this._pointerHoverObserver.observe(this.renderRoot,{childList:!0,subtree:!0})),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),y_(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())}),this.hass&&this._ensureHaRegistryAuthority(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>this._referencedContentUrls()),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e){Ag.add(e);const t=Og();return"undefined"==typeof window&&e(t),()=>{Ag.delete(e)}}(this._onLabsSnapshot),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},lf)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._loadOk&&this._ensureLiveSyncSubscriptions(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0)),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){this._liveRt?.dispose(),this._editorRuntime?._disposeLiveEditor(),this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._connectedPath&&location.pathname!==this._connectedPath&&this._leaveCardRoute(),window.removeEventListener("location-changed",this._onLocationChanged),window.removeEventListener("popstate",this._onLocationChanged),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._languageFailureUnsub?.(),this._languageFailureUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._warmModeRequest=0,this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._bootSettleRaf&&(cancelAnimationFrame(this._bootSettleRaf),this._bootSettleRaf=0),this._bootSettling=!1;for(const e of this._activityRt.values())clearTimeout(e.timer);window.removeEventListener("keydown",this._keyHandler),this._endTabDrag(),clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=void 0,this._tabSuppressClick=!1,clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null,this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,this._editorRuntimeLoadingVisible=!1,this._modeTransition.dispose(),this._cameraTransition.dispose(),this._cameraTransitionFit=null,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest++,this._slideTimer=void 0,this._slide="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),this._cancelDeviceDrag(),window.removeEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=void 0,clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),this._unsubVirtual&&(this._unsubVirtual(),this._unsubVirtual=null),this._unsubTrail&&(this._unsubTrail(),this._unsubTrail=void 0),this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=null,clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),qm(this._glowRuntimeState,this._glowRuntimeHost),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._touchContacts.clear(),this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._pointerHoverObserver?.disconnect(),this._pointerHoverObserver=void 0,this._pointerModality.disconnect(),this._editorSecondary?.reset(),this._resumeSettling=!1,this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_onKey(e){if("Escape"===e.key&&this._vacFit){if(this._vacFit.busy)return;return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void e.stopPropagation()}if("Escape"===e.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._vacCalConfirm)return void(this._vacCalConfirm.busy||(this._vacCalConfirm=null));if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void this._closeInfoCard();if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return this._alignDialog=null,void(this._preflightClipboardFallback=null);if(this._backupImportDialog)return void(this._backupImportDialog=null);if(this._backupExportDialog)return void(this._backupExportDialog=null);if(this._supportDialog)return void this._editorRuntime?._closeSupportDialog();if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void this._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary?.hasOpenGroup)return e.preventDefault(),void this._editorSecondary?.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),n=t.some(e=>e?.classList?.contains?.("editor-secondary")),r=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),s=/^[a-z]$/.test(o),a="z"===o||!s&&"KeyZ"===e.code,l="y"===o||!s&&"KeyY"===e.code,c=r&&(a&&e.shiftKey||l),h=r&&a&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry();if(("Delete"===e.key||"Backspace"===e.key)&&this._decorSel&&!i&&!n)return e.preventDefault(),void this._decorDeleteSel();const t="ArrowLeft"===e.key?[-this._gridPitch,0]:"ArrowRight"===e.key?[this._gridPitch,0]:"ArrowUp"===e.key?[0,-this._gridPitch]:"ArrowDown"===e.key?[0,this._gridPitch]:null;return!t||r||e.altKey||"select"!==this._decorTool||!this._decorSel||!this._decorList.some(e=>e.id===this._decorSel)||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag||i||n||this._editorSecondaryDialogBlocked?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._furnPalette=null,this._furnCategory=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorNudge(t[0],t[1]))}if("devices"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoDevicePosition()):h?(e.preventDefault(),void this._undoDevicePosition()):void("Escape"===e.key&&this._deviceDrag&&(e.preventDefault(),this._cancelDeviceDrag()))}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!n)return e.preventDefault(),void this._deletePhysicalSelection();if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():this._wallFaceBatch?(this._roomDialogCancel(),void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint())):"draw"===this._tool&&this._path.length?void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint()):"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry();if("Escape"===e.key)return this._physicalDrag||this._physicalRotate?(e.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(e.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length?(e.preventDefault(),void this._finishWallChain()):this._physicalSel?(e.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():("exit-tool"===this._resize?.escapeIdle()&&(this._tool="draw"),void this.requestUpdate())):"split"===this._tool?(e.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(e.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(e.preventDefault(),void(this._wallDialog=null)):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"column"!==this._tool||(e.preventDefault(),"opening"===this._tool&&this._clearOpeningPlacement(!0),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const e=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const t=this._curSpaceCfg,i=(t.room_drafts||[]).findIndex(e=>e.id===this._activeDraftId);if(i>=0){const n=Array.isArray(t.room_drafts[i]?.segments)?t.room_drafts[i].segments:[];this._path.length<2?(t.room_drafts.splice(i,1),t.room_drafts.length||delete t.room_drafts,this._activeDraftId=null):t.room_drafts[i]={id:this._activeDraftId,points:this._path.map(e=>[e[0]/yf,e[1]/yf]),segments:this._draftSegmentCms.map((e,t)=>({...n[t]||{},cm:e}))},this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),e)}return}this._path=this._path.slice(0,-1)}}_undoActiveDraftPoint(){const e=this._activeDraftId,t=this._path[0]?[...this._path[0]]:null,i=this._geometryHistory.undo();if(!i)return void this._undoPoint();if(!this._applyGeometryState(i.before,!0))return void this._geometryHistory.clear();const n=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;n?(this._activeDraftId=n.id,this._path=n.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,n.segments.map(e=>e.cm),n.id),this._resumeDraftBySpace[this._space]=n.id):(this._activeDraftId=null,this._path=t?[t]:[],this._draftSegmentCms=[],e&&this._resumeDraftBySpace[this._space]===e&&delete this._resumeDraftBySpace[this._space]),this._clearPlanSnapHover(),this._showToast(this._t("history.undone",{name:i.name}))}static async getConfigElement(){return await import("./editor-BVOuJXsz.js"),document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of cf.values())for(const t of e)clearTimeout(t.evict);cf.clear(),df=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const n of cf.values())for(const r of n)e++,r.dlg&&(t++,i.push(r.dlg.kind));return{keys:cf.size,slots:e,dlgs:t,drafts:i}}setConfig(e){const t=this._config,i=!!t&&Object.prototype.hasOwnProperty.call(t,"floor");this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...e};(i!==this._hasFixedFloor||t?.floor!==this._config.floor)&&(this._hashApplied=!1,this._navApplied=!1,this._warmVpArmed=!1),this._config.kiosk&&(this._booting=!1,this._bootFading=!1),!this._hasFixedFloor&&e.default_floor&&this._commitSpace(e.default_floor,!0);try{this._zoomBySpace=JSON.parse(localStorage.getItem(mf)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(ff)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=Og();try{const e=JSON.parse(localStorage.getItem(gf)||"null");this._kioskScale={icon:kn(e?.icon),font:kn(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(pf)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._seedDecorStyle(this._serverCfg),this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||El(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||El(this._layout),this._virtualLights=Yc(e.virtual_lights,this._cfgRev),this._serverStorage=!0)}catch{}this._adoptInitialSpace(this._model,this._loadOk),"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._syncCycleTimer(),this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const e=kg(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),n=cf.get(e);if(!n||!n.length)return;const r=n.find(e=>e.owner===this._warmGen);if(r)return this._warmLongReturn=!!r.freed&&Date.now()-r.freed>=zl,clearTimeout(r.evict),r.evict=0,r.freed=0,r.live=!0,this._warmSlot=r,this._warmKey=e,r.frameFingerprint&&this._continuity.adoptCompleteFrame(r.frameFingerprint),void(!this._devices.length&&r.devices?.length&&(this._devices=[...r.devices]));const{slot:o,sure:s}=((e,t,i,n)=>{const r=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===n?4:e.live?0:t?3:2};let o=null,s=0,a=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=r(i);e<=0||(e>s?(o=i,s=e,a=1):e===s&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(n,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=zl,this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},lf)),this._warmKey=e,s?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=t?new WeakRef(t):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,o.frameFingerprint&&this._continuity.adoptCompleteFrame(o.frameFingerprint),!this._devices.length&&o.devices?.length&&(this._devices=[...o.devices]),this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,path:location.pathname,place:t?new WeakRef(t):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.stageH,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0},n.push(this._warmSlot),this._warmTrim(n)))}_warmIdx(e){const t=e?.children;if(!t)return-1;for(let e=0;e4;){const t=e.findIndex(e=>!e.live);if(t<0)break;clearTimeout(e[t].evict),e.splice(t,1)}}_warmScheduleEvict(e,t){if(clearTimeout(e.evict),!(e.dlg||e.vp&&"view"!==e.vp.mode))return;const i=e.freed,n=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==n)return;var r;e.dlg=null,e.vp=(r=e.vp)&&"view"!==r.mode?{...r,mode:"view",zoom:r.snap?.space===r.space?r.snap.zoom:r.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:r,e.frameFingerprint="";const o=cf.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},df+250)}_warmAdoptViewport(e){const t=this._warmVp;if(!t||this._warmSlot?.path!==location.pathname)return void(this._warmVp=null);const i=this._fixedFloorState();if(this._hashApplied||!this._model.find(e=>e.id===t.space)||"valid"===i.kind&&i.id!==t.space||this._hasFixedFloor&&"valid"!==i.kind)return void(this._warmVp=null);this._commitSpace(t.space,!0),this._navApplied=!0;const n="view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view";this._adoptMode("view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const r=this._effectiveProjection(),o=r===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=o&&t.view?{...t.view}:null,this._viewModeSnap=o&&t.snap?{...t.snap}:null,!o&&t.logicalCenter){const e="iso"===r?rg([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var s;this._tool="opening"===(s=function(e){return"partition"===e?"draw":e}(s=t.tool))?"draw":"string"==typeof s&&wg.has(s)?s:"draw",this._decorTool=t.decorTool,this._showHidden=t.showHidden,this._showFar!==t.showFar&&(this._showFar=t.showFar,this._frame=null),this._selId=t.selId,this._resize?.restoreSelection(t.rszSel),this._decorSel=t.decorSel,this._warmVpArmed=!0,"view"!==n&&this._requestMode(n,!1,!0)}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=kg(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!t)return;const e=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,path:location.pathname,place:e?new WeakRef(e):null,idx:this._warmIdx(e),live:!0,hdrH:this._hdrH,stageH:0,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0};const n=cf.get(i)||[];for(n.push(this._warmSlot),cf.set(i,n),this._warmTrim(n);cf.size>8;){const e=cf.keys().next().value;if(void 0===e||e===i)break;for(const t of cf.get(e)||[])clearTimeout(t.evict);cf.delete(e)}}Object.assign(this._warmSlot,e)}}_warmViewportState(){const e=this._effectiveProjection();return{space:this._space,mode:this._mode,projection:e,activeLabsIso:this._labsIso,logicalCenter:this._logicalViewCenter(e),zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._resize?.selectedRoomId,decorSel:this._decorSel}}_warmDialogState(){const e=(e,t)=>({kind:e,space:this._space,mode:this._mode,data:t});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog||this._backupExportDialog||this._backupImportDialog?null:this._openingInfo?e("openingInfo",this._openingInfo.id):this._infoCard?e("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:e("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:e("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:e("marker",this._markerDialog):this._openingDialog?e("opening",this._openingDialog):this._backdropDialog?e("backdrop",this._backdropDialog):this._decorShapeDialog?e("decorShape",this._decorShapeDialog):this._decorTextDialog?e("decorText",this._decorTextDialog):this._roomDialog?e("room",{editId:this._roomEditId,fill:this._roomFill,customFill:this._roomCustomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,wallFaceBatch:this._wallFaceBatch,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:e("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._config?.kiosk||"steady"!==this._continuity.state)return;const e={vp:this._warmViewportState(),frameFingerprint:this._continuity.frameFingerprint,devices:this._devices};if(this._warmRevivePending||(e.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const t=this.parentNode;e.place=t?new WeakRef(t):null,e.idx=this._warmIdx(t)}this._warmPatch(e)}_warmReviveDialog(){const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return void(this._warmRevivePending=!1);const t=e.dlg,i=e.freed;if(t.mode===this._mode||"view"===t.mode||this._warmVp?.mode!==t.mode||this._editorRuntime){if(this._warmRevivePending=!1,e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>df)&&t.space===this._space&&t.mode===this._mode){switch(t.kind){case"space":this._spaceDialog={...t.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...t.data,busy:!1};break;case"settings":this._settingsDialog={...t.data,busy:!1};break;case"rules":this._rulesDialog={...t.data,busy:!1};break;case"opening":this._openingDialog={...t.data};break;case"backdrop":this._backdropDialog={...t.data};break;case"decorShape":this._decorShapeDialog={...t.data};break;case"decorText":{this._decorTextDialog={...t.data};const e=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:e,end:e};break}case"room":{const e=t.data;this._roomEditId=e.editId,this._roomFill=e.fill,this._roomCustomFill=e.customFill||null,this._roomTempSrc=e.tempSrc,this._roomHumSrc=e.humSrc,this._roomSrcOpen=e.srcOpen,this._roomSrcFilter=e.srcFilter,this._roomNameScale=e.nameScale,this._roomLabelScale=e.labelScale,this._areaSel=e.areaSel,this._nameSel=e.nameSel,this._pendingSplit=e.pendingSplit,this._wallFaceBatch=e.wallFaceBatch||null,this._path=e.path,this._wallFaceBatch&&(this._activeDraftId=this._wallFaceBatch.activeDraftId,this._draftSegmentCms=[...this._wallFaceBatch.activeCms]),this._roomDialog=!0;break}case"info":{const e=this._devices.find(e=>e.id===t.data);e&&(this._infoCard=e);break}case"openingInfo":{const e=(this._curSpaceCfg?.openings||[]).find(e=>e.id===t.data);e&&(this._openingInfo=e);break}}this.requestUpdate()}}else this._warmRevivePending=!0}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=El(this._serverCfg),this._layoutContentFingerprint=El(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const n=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(Qc).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>n.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(pf,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,config_fingerprint:this._cfgContentFingerprint,layout:this._layout,layout_rev:this._layoutRev,layout_fingerprint:this._layoutContentFingerprint,virtual_lights:Zc(this._virtualLights)}))}catch{}}_beginContinuityCandidate(e,t,i="plan"){return this._booting&&!this._continuity.hasCompleteFrame?this._continuity.token:(this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._resumeSettling=!0,this._continuity.beginCandidate(e,i))}_continuityStageValid(){const e=this._stageEl;return!!e&&e.clientWidth>0&&e.clientHeight>0}_continuityAssetsReady(){if(!this._model.length)return!1;const e=this._model.length?this._spaceModel():null;return!e?.bg?.href||this._signer.isReady(this.hass,e.bg.href)}_initialSpaceSelection(e,t=this._loadOk){const i=this._fixedFloorState(e,t);return"valid"===i.kind?{id:i.id,source:"fixed"}:this._hasFixedFloor?{id:null,source:"none"}:function(e){const t=new Set(e.spaceIds.filter(e=>!!e));if(!t.size)return{id:null,source:"none"};const i=[["hash",!1===e.acceptHash?null:e.hashSpace],["current",e.preserveCurrent?e.currentSpace:null],["saved",e.savedSpace],["default",e.defaultSpace],["first",e.spaceIds[0]]];for(const[e,n]of i)if(n&&t.has(n))return{id:n,source:e};return{id:null,source:"none"}}({spaceIds:e.map(e=>e.id),hashSpace:this._hashSpace(),acceptHash:!this._hashApplied,currentSpace:this._space,preserveCurrent:this._hashApplied||this._navApplied||this._warmVpArmed,savedSpace:this._savedNav()?.space,defaultSpace:this._config?.default_floor})}_adoptInitialSpace(e,t=this._loadOk){const i=this._initialSpaceSelection(e,t);return i.id?(this._commitSpace(i.id,!0),"hash"===i.source&&(this._hashApplied=!0),"saved"===i.source&&(this._navApplied=!0),i):i}_candidateBackdrop(e,t=this._space){const i=Da(e),n=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==n.kind)return"";const r=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===r)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return Nl([this._cfgRev,this._cfgContentFingerprint||El(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||El(this._layout),this._space,this._mode,this._view,t,this._glowScreenBlend?"screen":"normal",this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_settleContinuityFrame(){if(this._booting||!this._continuityStageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state)return void(this._continuityAssetsReady()&&(this._renderSnapshotAt=Date.now(),this._continuity.markCompleteFrame(this._visualFrameFingerprint())));if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._continuityStageValid(),assetsReady:()=>this._continuityAssetsReady(),frameFingerprint:()=>this._visualFrameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._resumeSettling=!1,this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._warmSnapshot()):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}_onBackdropLoaded(e,t){this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return V;const e="connection"===this._continuity.recoveryReason;return W`
e.stopPropagation()} @click=${e=>e.stopPropagation()} @@ -4948,7 +4948,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef aria-label=${this._t("editor.loading_aria")}> ${this._t("editor.loading")} -
`:V}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const e=this._serverCfg?.spaces||[];let t=e.length+":";for(const i of e){t+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const e of i.rooms||[]){const i=e.poly?.[0],n=e.poly?.[e.poly.length-1];t+=(e.poly?.length||0)+"."+(e.id||"")+"."+(e.open_to||[]).join("+")+"."+(e.area||"")+"."+JSON.stringify(e.settings||0)+"."+(e.x??"")+","+(e.y??"")+","+(e.w??"")+","+(e.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(n?n[0]+"/"+n[1]:"")+";"}}return t}get _model(){if(!this._serverCfg)return[];const e=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===e)return this._modelCache.model;const t=this._buildModel();return this._modelCache={key:e,model:t},t}_buildModel(){if(!this._serverCfg)return[];const e=this._renderCfg;return Ta(e).map((t,i)=>{const n=e.spaces[i]?.plan_url;return t.bg&&n?{...t,bg:{...t.bg,href:n}}:t})}_spaceModel(){return this._hasFixedFloor?Ll(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Ll(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!0,this._clearRoomFocus(!0);for(const e of this._pointers.keys())for(const t of this.renderRoot.querySelectorAll("*"))try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._drag=null,this._deviceDrag=null,this._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary?.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=cd({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!t.length&&e.settings?.filter_seeded)return;e.markers=e.markers||[];const i=[];for(const n of t){const t="h"+n.slice(n.indexOf(":")+1);e.markers.push({id:t,binding:n,hidden:!0}),i.push(n.slice(n.indexOf(":")+1))}const n={...e.settings||{},filter_seeded:!0};delete n.show_all,i.length&&Array.isArray(n.new_device_ids)&&(n.new_device_ids=n.new_device_ids.filter(e=>!i.includes(e))),e.settings=n,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const e=this._settings.icon_rules;if(!e||!Array.isArray(e)||!e.length)return;const t=JSON.stringify(e);return t!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=t,this._rulesCompiled=ye(e)),this._rulesCompiled}get _fillColors(){return Xi(this._settings)}get _excluded(){return fd(this._settings)}_setAreaLifecycleConfig(e){this._cfgEpochPreservedConfig=e,this._serverCfg=e}willUpdate(e){if(e.has("_serverCfg")){const e=this._cfgEpochPreservedConfig===this._serverCfg;this._cfgEpochPreservedConfig=null,e||this._cfgEpoch++}this._syncEmptySpaceState(),this._dangerConfirm&&(this._dangerConfirmMissingSpace()||"warm"===this._syncDangerConfirmLocaleGate())&&this._cancelDangerConfirm(),e.has("hass")&&this.hass&&(this._hassSequence++,this._renderSnapshotAt=Date.now(),this._continuity.note("hass-snapshot"),this._ensureHaRegistryAuthority(),this._planHassMemo=null,this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick()),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._visualFrameFingerprint()),this._captureRenderDeviceSnapshot()}updated(){this._pruneDevicePressFeedback(),this._syncDayCycleClock(),this._warmSnapshot(),this._editorRuntime&&this._dtMeasure();const e=this._stageEl;e&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(e)),e&&this._booting&&!this._bootTimer&&this._bootWatch();const t=this.renderRoot.querySelector(".hdr");if(t&&e&&!this._roHdr){const i=()=>{const t=this.renderRoot.querySelector("ha-card");if(!t)return;const i=e.getBoundingClientRect().top-t.getBoundingClientRect().top,n=Math.min(Math.max(t.getBoundingClientRect().top,0),120),r=Math.round(i+n);r>=0&&r!==this._hdrH&&(this._hdrH=r),r>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:r,stageH:e.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(t),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(e&&!this._view&&this._refitView(),this._editorSecondary?.afterRender(),this._settleContinuityFrame(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const e=function(e){const t=e?.floors;if(!t||"object"!=typeof t)return[];const i=[];for(const e of Object.values(t))e&&e.floor_id&&i.push({id:e.floor_id,name:e.name||e.floor_id,level:e.level??null});return i.sort((e,t)=>{const i=e.level??1e9,n=t.level??1e9;return i!==n?i-n:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptConfigCapabilities(e){const t=e&&"object"==typeof e?e:{};this._haIntegrationVersion="string"==typeof t.integration_version?t.integration_version:this._haIntegrationVersion;const i=t.support_api;this._haSupportApi="number"==typeof i&&Number.isSafeInteger(i)?i:null,this._haDecorAssetsApi=1===t.decor_assets_api?1:null}async _syncDecorAssets(e){const t=++this._decorAssetSyncToken;if(1!==this._haDecorAssetsApi||!this.hass)return void(this._decorAssets=new Map);const i=await sm(this.hass,nm(e),this._cfgRev);t===this._decorAssetSyncToken&&(this._decorAssets=i,this._resign(),this.requestUpdate())}_adoptStructuralResponses(e,t,i){const n=e?.config,r=n&&Array.isArray(n.spaces)?n:null,o=Il(r),s=o!==(this._cfgContentFingerprint||Il(this._serverCfg));if(s&&(this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._pendingPhysicalWrites.clear(),this._clearRoomFocus(!0),this._cancelCameraTransition(!1),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=r,this._seedDecorStyle(this._serverCfg),this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=Yc(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);Zc(t)!==Zc(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let a=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},n=Il(e);a=n!==(this._layoutContentFingerprint||Il(this._layout)),a&&(this._cancelCameraTransition(!1),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._layout=e,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._adoptConfigCapabilities(e),this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),s&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),a&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:s,layoutChanged:a}}_resumePendingNavMode(){if(!this._pendingNavMode||!this._canEdit||this._config?.kiosk)return!1;const e=this._pendingNavMode;return this._pendingNavMode=null,this._setMode(e,!1),!0}async _loadFromServer(){this._loading=!0,this._loadTries++;const e=this._space,t=!!this._view;try{const[i,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),r=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Il(r)!==(this._cfgContentFingerprint||Il(this._serverCfg))||Il(n?.layout??{})!==(this._layoutContentFingerprint||Il(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(r)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0)}o&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),this._connectionWasLost=!1,this._serverStorage=!0,"boolean"==typeof i?.can_write&&(this._serverCanWrite=i.can_write),this._canOptimizeUndo=!(!i?.can_optimize_undo&&!n?.can_optimize_undo),this._adoptStructuralResponses(i,n),this._syncDecorAssets(r).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:t&&this._space===e||this._restoreZoom(),this._loadOk=!0,this.hass.callWS({type:"houseplan/trail/get"}).then(e=>{this._vacSrvTrails=e?.trails||{},this.requestUpdate()}).catch(()=>{}),this._ensureLiveSyncSubscriptions()}catch(e){if(this._serverCfg)this._scheduleLoadRetry(!0);else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(lf)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1,this._continuityDataReady=!0,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}}_ensureLiveSyncSubscriptions(){const e=this.hass?.connection;if(!e)return;if(e!==this._liveSyncConnection&&(this._unsubCfg?.(),this._unsubCfg=null,this._unsubLayout?.(),this._unsubLayout=null,this._unsubTrail?.(),this._unsubTrail=void 0,this._unsubVirtual?.(),this._unsubVirtual=null,this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=e),this._liveSyncAttempt)return;const t=this._liveSyncGeneration,i=[],n=(n,r,o,s)=>{n()||i.push(async()=>{const i=await e.subscribeEvents(s,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!n()?r(i):i?.()})};if(n(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),n(()=>this._unsubTrail,e=>{this._unsubTrail=e},"houseplan_trail_updated",async()=>{try{const e=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=e?.trails||{},this.requestUpdate()}catch{}}),n(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),n(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=eh(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const r=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=r,r.finally(()=>{this._liveSyncAttempt===r&&(this._liveSyncAttempt=null)})}async _reloadConfigOnly(e=!1,t){if(!e){if(void 0!==t&&t<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,t),400))}this._beginContinuityCandidate("config-reload",!1);try{const e=await this.hass.callWS({type:"houseplan/config/get"}),t=e?.config&&Array.isArray(e.config.spaces)?e.config:null;if(Il(t)!==(this._cfgContentFingerprint||Il(this._serverCfg))&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0);const i=this._space;this._adoptStructuralResponses(e),this._syncDecorAssets(t).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._space!==i&&this._restoreZoom(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(e){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(e)}))}finally{this._continuityDataReady=!0,this.requestUpdate()}}_scheduleLoadRetry(e=!1){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,!e&&this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRev=-1,this._haBindingCacheKey="",this._areaSnapshotCleanupCandidates.clear(),this._planHassMemo=null,this._haRegistryRelease=dh(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return ph(this.hass)}get _planHass(){const e=this._haRegistry,t=_h(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const n=fh(this.hass,e),r=vh(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:n,full:r},n}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRt.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+l_)>e?1:0)).join("|"),i=Zc(this._virtualLights);if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotLayout===this._layout&&this._capturedSnapshotConfigEpoch===this._cfgEpoch&&this._capturedSnapshotVirtual===i&&this._capturedSnapshotActivity===t)return;const n=this._planHass,r=new Map,o=new Map,s=new Set(["sun.sun"]);this._vacFit?.source&&s.add(this._vacFit.source);const a=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void s.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?a.add(n):"entity"===i&&s.add(n)};for(const e of this._model)for(const t of e.rooms)t.area&&l.add(t.area),c(t.settings?.temp_source),c(t.settings?.hum_source);for(const e of this._serverCfg?.spaces||[])for(const t of e.openings||[])for(const e of Kt(t))s.add(e);for(const e of this._serverCfg?.spaces||[])for(const t of e.decor||[])if("text"===t.kind){for(const e of String(t.text||"").matchAll(/\{([^{}\r\n]+)\}/g)){const t=Di(e[1]);t?.entity&&s.add(t.entity)}t.entity&&s.add(t.entity),o.set(`decor:${e.id}:${t.id}`,Oi(t.text,t,n,e=>!!n.entities?.[e]&&!!n.states?.[e]&&!ad(n,e,sd(this._markers))))}const h=Vh(n,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])r.set(pp(e.id,t),cp(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:h,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}));if(this._isVacDev(e)){const t=this._vacSource(e,n),i=t?Ql(n?.states?.[t]?.attributes):null,r=this._vacRt.get(e.id),s=fc(e.id,e.marker?.vacuum??null,e.space,t),a=yc({routes:s,observed:bc(s,[t],t=>this._vacObservedMapId(e,t,n)),spaceIds:new Set(this._model.map(e=>e.id))});o.set(`vacuum:${e.id}`,{source:t,telemetry:i,routes:s,resolution:a,mapId:i?this._vacMapId(e,i,n):null,runtime:r?{trail:r.trail,lastTs:r.lastTs,moving:r.moving,jump:r.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=up({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,positions:(u=this._model.length>0,p=this._devices,m=e=>this._livePos(e),u?new Map(p.map(e=>[e.id,m(e)])):new Map),facts:o,entityIds:s,deviceIds:a,areaIds:l});var u,p,m;this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotLayout=this._layout,this._capturedSnapshotActivity=t,this._capturedSnapshotConfigEpoch=this._cfgEpoch,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=d:(this._visibleDeviceSnapshot=d,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}get _renderPlanHass(){return this._renderDeviceSnapshot?.hass||this._planHass}get _renderDevices(){return this._renderDeviceSnapshot?.devices||this._devices}get _renderVacuumDevices(){return this._renderDeviceSnapshot?.vacuumDevices||this._devices}get _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return wh(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=ph(e);return{access:t.access,authoritative:t.authoritative,revision:t.revision,lastSuccess:t.lastSuccess,error:t.error}}(this.hass),t={active:0,ha_disabled:0,orphaned:0,unverified:0};for(const e of this._markers)e.removed||"virtual"===e.binding||t[this._bindingStatus(e.binding).kind]++;return{registry:{...e,lastSuccessAgeMs:e.lastSuccess?Math.max(0,Date.now()-e.lastSuccess):null},bindings:t}}_openBindingInHa(e){const[t,i]=e.split(":");if(i)if("device"!==t){if("entity"===t){const e=this._fullRegistryHass.entities?.[i];e?.device_id&&vf("/config/devices/device/"+encodeURIComponent(e.device_id))}}else vf("/config/devices/device/"+encodeURIComponent(i))}_bindingHasHaPage(e){const[t,i]=e.split(":");return!!i&&("device"===t||"entity"===t&&!!this._fullRegistryHass.entities?.[i]?.device_id)}_toggleMarkerDialogVisibility(){const e=this._markerDialog;if(!e)return;const t="ha"===e.bindingMode?this._bindingStatus(e.binding):null,i=e.hideFromPlan||"ha_disabled"===t?.kind;i&&"ha_disabled"===t?.kind?this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device")):i&&"unverified"===t?.kind?this._showToast(this._t("toast.ha_binding_unverified")):this._markerDialog={...e,hideFromPlan:!i}}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}_display(e){return this._signer.display(this.hass,e)}_referencedContentUrls(){const e=fn(this._serverCfg);for(const t of this._decorAssets.values())e.add(t.url);return e}_resign(){this._signer.resign(this.hass,this._referencedContentUrls())}_onLayoutEvent(e){e<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{e<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(e){const t=e?.rev;"number"==typeof t&&t>this._layoutRev&&(this._layoutRev=t)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;this._beginContinuityCandidate("layout-reload",!1);const e=new Map;for(const t of this._dirtyPos)this._layout[t]&&e.set(t,this._layout[t]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[t,i]of this._sentPos)e.set(t,i);try{const t=await this.hass.callWS({type:"houseplan/layout/get"}),i={...t?.layout||{}};for(const[t,n]of e)null===n?delete i[t]:i[t]=n;const n=Il(i);n!==Il(this._layout)&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear(),this._layout=i),this._layoutContentFingerprint=n,this._layoutRev=t?.rev??this._layoutRev,this._canOptimizeUndo=!!t?.can_optimize_undo,this._haIntegrationVersion="string"==typeof t?.integration_version?t.integration_version:this._haIntegrationVersion,this._undoKind=t?.undo_kind||null,this._cacheSnapshot(),this.requestUpdate()}catch{}finally{this._continuityDataReady=!0,this.requestUpdate()}}_persistLocalLayout(){this._layout=eo(Hr(this._layout)),localStorage.setItem(lf,JSON.stringify(this._layout))}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=_h(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+wu(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const n=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=dd({hass:e,registry:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:e=>this._t(e),iconRules:this._iconRules});const r=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+r.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const n of r)i.set(n,wh(e,n,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=bh(),i=Date.now();for(const[n,r]of e)"active"===r.kind?t[n]={kind:"active",ts:i}:"ha_disabled"===r.kind&&(t[n]={kind:"ha_disabled",reason:r.reason,ts:i});const n=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));oh=n;try{localStorage.setItem(rh,JSON.stringify(n))}catch{}}(i),this._haBindingCacheKey=o}let s=null;if(t.authoritative&&(s=this._resolveAreaRelocations(t),this._areaRelocationIds=new Set(s.relocateIds),this._areaRelocationIds.size)){this._cancelDeviceDrag();const e=this._areaRelocationIds;this._devicePositionHistory.removeWhere(({before:t,after:i})=>e.has(t.deviceId)||e.has(i.deviceId));const t=this._markerDialog;if(t?.devId&&!t.roomTouched&&this._areaRelocationIds.has(t.devId)){const e=this._devices.find(e=>e.id===t.devId);e&&(this._markerDialog={...t,room:e.marker?.room_id?`${e.space}#@${e.marker.room_id}`:e.space&&e.area?`${e.space}#${e.area}`:""})}}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),s&&this._syncAreaRelocations(s),this._syncActivityRuntime();const a=new Set(this._devices.filter(e=>!e.hidden).map(e=>e.id));for(const e of this._vacRt.keys())a.has(e)||this._vacRt.delete(e);if(this._infoCard){const e=this._devices.find(e=>e.id===this._infoCard.id);this._infoCard=e&&"ha_disabled"!==e.bindingStatus?.kind?e:null}const l=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==n.get(e.id));l&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._deviceDrag&&"ha_disabled"===this._devices.find(e=>e.id===this._deviceDrag.id)?.bindingStatus?.kind&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear()),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(ff(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_resolveAreaRelocations(e=this._haRegistry){const t=function(e){const t=Gp(e.snapshot),i=new Set(Object.values(t).map(e=>e.binding)),n=new Map;for(const[t,r]of e.previousCandidates||[])Vp(t)&&Number.isFinite(r)&&i.has(t)&&n.set(t,r);const r=new Set,o=new Set;for(const t of e.markers||[])t.removed||(Up(t.id)&&r.add(t.id),Vp(t.binding)&&o.add(t.binding));const s=new Set(Object.keys(e.registryDevices||{})),a=new Set(Object.keys(e.registryEntities||{})),l=new Set(Object.keys(e.liveStates||{})),c=new Map;for(const[e,i]of Object.entries(t)){const t=c.get(i.binding)||[];t.push(e),c.set(i.binding,t)}const h=new Set;let d=!1;for(const[t,i]of c){const[c,u]=Jp(t);if(o.has(t)||i.some(e=>r.has(e))||("device"===c?s.has(u):a.has(u)||l.has(u))){n.delete(t);continue}const p="device"===c?s.size>0:a.size>0;if(!e.authoritative||!p)continue;const m=n.get(t);if(void 0!==m){if(m!==e.revision)for(const e of i)h.add(e)}else n.set(t,e.revision),d=!0}return{removeIds:h,candidates:n,needsConfirmationRefresh:d}}({snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,revision:e.revision,registryDevices:e.devices,registryEntities:e.entities,liveStates:this.hass?.states,markers:this._markers,previousCandidates:this._areaSnapshotCleanupCandidates});return this._areaSnapshotCleanupCandidates=t.candidates,t.needsConfirmationRefresh&&this._canEdit&&uh(this.hass),Qp({devices:this._devices,model:this._model,layout:this._layout,snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,cleanupSnapshotIds:t.removeIds,coordinateScale:_f})}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const e=this._devices.filter(e=>!e.marker&&!e.virtual).map(e=>e.id).sort(),t=e.join(",");if(t===this._newSyncKey)return;this._newSyncKey=t;const i=this._settings,{fresh:n,known:r}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),n=e.filter(e=>!i.has(e));return{fresh:n,known:n.length?[...t,...n]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||n.length){const e=[...new Set([...i.new_device_ids||[],...n])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:r,new_device_ids:e}},this._saveConfig()}}_syncAreaRelocations(e){if(!(this._serverCfg&&this._norm&&this._canEdit&&this._haRegistry.authoritative))return;const t=e.decisions.filter(e=>e.updateSnapshot||e.removeSnapshot);if(!t.length)return;const i=Il({snapshot:Gp(this._settings.marker_area_snapshot),decisions:t});i!==this._areaRelocationSyncKey&&(this._areaRelocationSyncKey=i,this._areaRelocationWrite=this._areaRelocationWrite.catch(()=>{}).then(async()=>{if(!this._serverCfg||!this._haRegistry.authoritative)return;const e=this._resolveAreaRelocations();this._areaRelocationIds=new Set(e.relocateIds);const t=new Set,i=new Map;let n=!1;for(const r of e.decisions){if(!r.relocate)continue;const e=C_(this._layout,r.id);try{await this._persistDevicePlacement(r.id,null),t.add(r.id),e&&i.set(r.id,e),this._areaRelocationIds.delete(r.id)}catch(t){n=!0,this._layout=R_(this._layout,r.id,e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}}const r=this._settings,o=function(e,t,i=new Set){const n=Gp(e);for(const e of t.decisions)e.removeSnapshot?delete n[e.id]:e.updateSnapshot&&e.binding&&e.area&&(e.relocate&&!i.has(e.id)||(n[e.id]={binding:e.binding,area:e.area}));return n}(r.marker_area_snapshot,e,t),s=[...new Set([...Array.isArray(r.new_device_ids)?r.new_device_ids:[],...t])],a=Il(o)!==Il(Gp(r.marker_area_snapshot)),l=Il(s)!==Il(Array.isArray(r.new_device_ids)?r.new_device_ids:[]);if(a||l){const e=r.marker_area_snapshot,n=r.new_device_ids,a={...this._serverCfg,settings:{...r,marker_area_snapshot:o,...l?{new_device_ids:s}:{}}};this._setAreaLifecycleConfig(a);try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.cancel(),await this._writeConfig()}catch(r){const a=this._settings;if(Il(a.marker_area_snapshot)===Il(o)&&Il(a.new_device_ids)===Il(l?s:n)){const t={...a};void 0===e?delete t.marker_area_snapshot:t.marker_area_snapshot=e,void 0===n?delete t.new_device_ids:t.new_device_ids=n;const i={...this._serverCfg,settings:t};this._setAreaLifecycleConfig(i),this._cfgContentFingerprint=Il(this._serverCfg)}for(const e of t)this._areaRelocationIds.add(e);const c=new Set;for(const[e,t]of i)try{await this._persistDevicePlacement(e,t)}catch(t){c.add(e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}this._areaRelocationSyncKey="",this._regSignature="";if("conflict"===(r&&"object"==typeof r&&"code"in r?r.code:void 0)?(this._showToast(this._t("toast.conflict")),await this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(r)})),c.size&&this._serverCfg){const e=this._settings,t=[...new Set([...Array.isArray(e.new_device_ids)?e.new_device_ids:[],...c])],i={...this._serverCfg,settings:{...e,new_device_ids:t}};this._setAreaLifecycleConfig(i);try{await this._writeConfig()}catch(e){this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)}))}}}}n&&(this._areaRelocationSyncKey=""),this.requestUpdate()}))}get _newIds(){const e=this._settings.new_device_ids;return new Set([...Array.isArray(e)?e:[],...this._areaRelocationIds])}_ackNewDevice(e){if(!this._newIds.has(e)||!this._serverCfg)return;const t=this._settings;this._serverCfg={...this._serverCfg,settings:{...t,new_device_ids:(t.new_device_ids||[]).filter(t=>t!==e)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(e){if(!e)return null;const t=[];for(const i of this._renderDevices){if(i.area!==e||i.virtual)continue;const n=Yh(this._renderPlanHass,i.entities);null!=n&&t.push(n)}return fi(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}_defaultPositions(){const e={},t=this._config?.icon_size??2.5;for(const i of this._model){const n=t/100*tl(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const r=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!r.length)continue;const o=this._roomBounds(t),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(r.length*a/Math.max(l,1)))),h=Math.ceil(r.length/c),d=a/c,u=l/Math.max(h,1),p=r.map((e,t)=>({x:o.x+s+d*(t%c+.5),y:o.y+s+u*(Math.floor(t/c)+.5)}));yi(p,o,n,.5*s),r.forEach((t,i)=>e[t.id]=Ia(p[i]))}}return e}_pos(e){const t=this._renderDeviceSnapshot?.positions.get(e.id);return t?{x:t.x,y:t.y}:this._livePos(e)}_livePos(e){const t=this._spaceModelById(e.space),i=this._areaRelocationIds.has(e.id)?void 0:this._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*_f,y:i.y*_f}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Ia(el(t)):{x:500,y:500}}_devicePlacementForCanvas(e,t,i){if(!this._norm)return{x:Math.round(t),y:Math.round(i)};const n=this._gridPitch,r=Math.round(t/n)*n,o=Math.round(i/n)*n;return{s:e.space,x:Na(r/_f),y:Na(o/_f)}}_previewDevicePlacement(e,t){this._layout=R_(this._layout,e,t),this.requestUpdate()}_cancelDeviceDrag(){const e=this._deviceDrag;if(!e)return!1;this._deviceDrag=null;try{e.source?.hasPointerCapture?.(e.pointerId)&&e.source.releasePointerCapture(e.pointerId)}catch{}return this._previewDevicePlacement(e.id,e.before),!0}async _persistDevicePlacement(e,t){if(this._layout=R_(this._layout,e,t),this._serverStorage){let i=null,n=!1;try{let r;if(null===t)i=null,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/delete",device_id:e});else{const t=Qr(this._layout[e]);Il(t)!==Il(this._layout[e])&&(this._layout={...this._layout,[e]:t}),i=t,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t})}this._noteLayoutRev(r),this._layoutContentFingerprint=Il(this._layout),this._cacheSnapshot()}finally{n&&this._sentPos.get(e)===i&&this._sentPos.delete(e)}return}this._persistLocalLayout()}_devicePositionStateValid(e){const t=this._devices.find(t=>t.id===e.deviceId);return!!t&&t.space===e.spaceId&&"ha_disabled"!==t.bindingStatus?.kind&&!!this._spaceModelById(e.spaceId)}async _runDevicePositionHistory(e){if(this._cancelDeviceDrag()||this._devicePositionBusy)return;const t="undo"===e?this._devicePositionHistory.undo():this._devicePositionHistory.redo();if(!t)return;const i="undo"===e?t.before:t.after;if(!this._devicePositionStateValid(i))return this._devicePositionHistory.clear(),this._showToast(this._t("history.device_stale")),void this.requestUpdate();const n=C_(this._layout,i.deviceId);i.spaceId!==this._space&&(this._commitSpace(i.spaceId),this._restoreZoom()),this._devicePositionBusy=!0,this._previewDevicePlacement(i.deviceId,i.placement);try{await this._persistDevicePlacement(i.deviceId,i.placement),this._showToast(this._t("undo"===e?"history.undone":"history.redone",{name:t.name}))}catch(t){this._previewDevicePlacement(i.deviceId,n),"undo"===e?this._devicePositionHistory.redo():this._devicePositionHistory.undo(),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}finally{this._devicePositionBusy=!1,this.requestUpdate()}}_undoDevicePosition(){this._runDevicePositionHistory("undo")}_redoDevicePosition(){this._runDevicePositionHistory("redo")}_savePos(e,t,i){this._spaceModelById(e.space)&&(this._layout=R_(this._layout,e.id,this._devicePlacementForCanvas(e,t,i)),this._dirtyPos.add(e.id),this._persistLayout())}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const n=i?null:this._renderDeviceSnapshot?.presentations.get(pp(e.id,t));return n||cp(this._renderPlanHass,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,designPreview:i,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._renderDevices,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion})}_deviceVisual(e){return this._devicePresentation(e).visual}_stateClass(e,t=this._deviceVisual(e)){const i=this._devicePresentation(e);return i.effectiveHidden?"":("icon_ripple"!==i.display||!1===this._config?.live_states||"alarm"===t.status||t.activity,lp({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=Zh(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:Xh(this._renderPlanHass,e.entities)}_bindingEntities(e){if("virtual"===e||!e)return[];const t=this._bindingStatus(e);return"active"===t.kind?t.enabledEntityIds:t.allEntityIds}_bindingHasClimate(e){return this._bindingEntities(e).some(e=>e.startsWith("climate."))}_bindingHasAlarm(e){return this._bindingEntities(e).some(e=>{const t=this._planHass?.states?.[e]||this.hass?.states?.[e],i=this._fullRegistryHass.entities[e]||this.hass?.entities?.[e];return Rn(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_deviceBindingActive(e,t=!0){if(e.virtual||"virtual"===e.bindingKind)return!0;if(!e.bindingKind||!e.bindingRef)return!1;const i=this._bindingStatus(`${e.bindingKind}:${e.bindingRef}`);return"active"===i.kind||(t&&this._showToast(this._t("ha_disabled"===i.kind?"toast.ha_disabled_action":"toast.ha_binding_unverified")),!1)}_openMoreInfo(e){e?this._planEntityAvailable(e)?(this._nativeMoreInfoEntity=e,ff(this,"hass-more-info",{entityId:e})):this._showToast(this._t("toast.ha_disabled_action")):this._showToast(this._t("toast.no_entity"))}_interruptViewGesture(e,t){if(clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),void 0!==e)for(const i of[t,this._stageEl])try{i?.hasPointerCapture?.(e)&&i.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._roomPointer=null,this._doubleFit.clear()}_closeInfoCard(){this._interruptViewGesture(),this._holdFired=!1,this._infoCard=null}_ctxDevice(e,t){"view"===this._mode&&(e.preventDefault(),e.stopPropagation(),this._deviceBindingActive(t)&&(t.primary?this._openMoreInfo(t.primary):this._infoCard=t))}_clickDevice(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if(e.stopPropagation(),this._deviceDrag?.moved||this._suppressClick||this._holdFired)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const n=Nd(i.tapAction,i.primary?.split(".")[0]);if("none"===n)return;const r=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===n){const e=this._toggleIntent(i);if(!e)return;if(!jd(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=jd(e);if(!t)return;if("virtual-light"===t.kind)return this._startDevicePressFeedback(i.id),void this.hass.callWS({type:"houseplan/virtual_light/toggle",marker_id:t.markerId}).then(e=>{const t=eh(this._virtualLights,e);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}).catch(e=>this._showToast(this._t("toast.virtual_light_toggle_failed",{err:this._errText(e)})));const{command:n}=t;this._startDevicePressFeedback(i.id),this.hass.callService(n.domain,n.service,n.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},n=((o=e).targets.length?o.targets:o.skippedTargets).map(e=>e.name||e.entityId||e.ref).join(", ")||i.name;if(i.marker?.tap_confirm){const r=this._toggleConfirmationLines(e);if(!r.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:n}),lines:r,initialIntent:e,deviceId:i.id,exec:()=>{const n=this._devices.find(e=>e.id===i.id),r=n?this._toggleIntent(n):null;r&&!jd(r)&&this._showUnavailableToggleTargets(r)||(r&&Vd(e,r)?t(r):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===n)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===n){const e=i.marker?.tap_target||"",t=function(e){const t=String(e||"").split(".")[0];return"automation"===t?{domain:"automation",service:"trigger"}:"script"===t?{domain:"script",service:"turn_on"}:"scene"===t?{domain:"scene",service:"turn_on"}:null}(e),n=this.hass.states[e];if(!t||!n)return void this._showToast(this._t("toast.run_target_missing"));const o=n.attributes?.friendly_name||e;return void r(this._t("confirm.tap_run",{name:o}),()=>{this._deviceBindingActive(i)&&this._planEntityAvailable(e)&&(this._startDevicePressFeedback(i.id),this.hass.callService(t.domain,t.service,{entity_id:e}).then(()=>{this._stampActivity(i.id,"event",this._activitySourceKey(i)),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)}))))})}"more-info"===n&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||jd(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Ud.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const n of e.skippedTargets){if(!Ud.has(n.reason))continue;const e=String(n.name||n.entityId||n.ref||"").trim();e&&!i.has(e)&&(i.add(e),t.push(e))}return t}(e);return!!t.length&&(this._showToast(1===t.length?this._t("toast.toggle_target_unavailable",{name:t[0]}):this._t("toast.toggle_targets_unavailable",{names:t.join(", ")})),!0)}_keyDevice(e,t){"Enter"!==e.key&&" "!==e.key||"view"!==this._mode&&"devices"!==this._mode||(e.preventDefault(),this._clickDevice(e,t))}_startDevicePressFeedback(e){const t=[...this.renderRoot.querySelectorAll(".dev[data-id]")].find(t=>t.dataset.id===e),i=t?.querySelector(".device-shell");if(!i||"function"!=typeof i.animate)return;const n=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),r=Number.isFinite(n)?Math.max(.95,Math.min(1,n)):1;this._devicePressAnimations.get(e)?.cancel();const o=this._reducedMotion?[{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"},{outlineColor:"var(--hp-accent, #3ea6ff)",outlineStyle:"solid",outlineWidth:"2px",outlineOffset:"2px",offset:.5},{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"}]:[{scale:String(r)},{scale:"0.95",offset:.5},{scale:"1"}],s=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,s);const a=()=>{this._devicePressAnimations.get(e)===s&&this._devicePressAnimations.delete(e)};s.addEventListener("finish",a,{once:!0}),s.addEventListener("cancel",a,{once:!0})}_cancelDevicePressFeedback(){for(const e of this._devicePressAnimations.values())e.cancel();this._devicePressAnimations.clear()}_pruneDevicePressFeedback(){if(!this._devicePressAnimations.size)return;const e=new Set([...this.renderRoot.querySelectorAll(".dev[data-id]")].map(e=>e.dataset.id||""));for(const[t,i]of this._devicePressAnimations)e.has(t)||(i.cancel(),this._devicePressAnimations.delete(t))}_t(e,t){return ku(wu(this.hass,this._config?.language),e,t)}get _colorPickerLabels(){return{title:this._t("color_picker.title"),hue:this._t("color_picker.hue"),saturation:this._t("color_picker.saturation"),value:this._t("color_picker.value"),hex:this._t("color_picker.hex"),invalidHex:this._t("color_picker.invalid_hex")}}_help(e){return this._editorRuntimeOrThrow()._help(e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(e){const t=[];for(const i of this._devices){if(i.space!==e.id||i.hidden)continue;const n=this._pos(i);t.push({minX:n.x,minY:n.y,maxX:n.x,maxY:n.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,n=Math.cos(i)*e.rlen/2,r=Math.sin(i)*e.rlen/2,o=qa([[e.rx-n,e.ry-r],[e.rx+n,e.ry+r]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){if("image"===e.kind&&!this._decorAssets.has(e.asset_id)&&"decor"!==this._mode)continue;const n=qa("line"===e.kind?[[e.x1*_f,e.y1*i],[e.x2*_f,e.y2*i]]:"text"===e.kind?[[e.x*_f,e.y*i]]:mr({x:e.x*_f,y:e.y*i,w:e.w*_f,h:e.h*i,angle:e.angle}));n&&t.push(n)}for(const i of this._physicalBodiesR(e)){const e=qa(i);e&&t.push(e)}}return Ga(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:_f,h:_f};return{rect:e,all:e,outliers:0}}const t=this._frame,i="view"!==this._mode;if(t&&t.id===e.id&&this._bdDrag)return t;if(t&&t.id===e.id&&t.model===e&&t.layout===this._layout&&t.devs===this._devices&&t.far===this._showFar&&t.grow===i)return t;const n=Za(this._contentItems(e));let r=n.all||Qa(e),o=this._showFar?r:n.core||Qa(e);return t&&t.id===e.id&&i&&t.grow&&(o=gf(t.rect,o),r=gf(t.all,r)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:r,outliers:n.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),n=this._openingsR.map((e,t)=>({id:String(e.id||t),sourceIndex:t,type:e.type,x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen>0?e.rlen:900,flipH:!!e.flip_h,flipV:!!e.flip_v})),r=ya(J_,this._cellCm),o=ya(10,this._cellCm),s=`${e.id}|${function(e){let t=2166136261;const i=e=>{if("number"!=typeof e)if("string"!=typeof e)if("boolean"!=typeof e)if(Array.isArray(e)){t=Ng(t,e.length);for(const t of e)i(t)}else{if(e&&"object"==typeof e){const t=e;for(const e of Object.keys(t).sort())i(e),i(t[e]);return}t=Ng(t,-1)}else t=Ng(t,e?1:0);else for(const i of e)t=Ng(t,i.codePointAt(0)||0);else t=Ng(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:n,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:Z_,wallHeight:r,floorEdgeHeight:o,algorithm:3})}`;return{key:s,build:()=>{const o=oa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,s=this._roomWallOpeningInputs(this._openingsR,e),a=t.length||o.length?Ps(e.rooms,t,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,_f,o):null;if(a&&("failed-core"===a.status||"not-applicable"===a.status))throw new Error("wall boolean geometry failed");const l=a?.paperGeom??Ts(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f);if(!l)throw new Error("floor boolean geometry failed");const c=a?.openingIndex||Ns(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f),h=n.map(e=>{const i=this._openingsR[e.sourceIndex],n="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?tu(i.partitionHost,n):t.length||"gate"===e.type?qs(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:n}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(Hg([e.x,e.y,e.angle,e.length,e.face.ox,e.face.oy,t])&&e.length>0&&t>0))throw new Error("invalid isometric opening input");const i=e.length/2;let n;if("passage"===e.type)n=[];else if("gate"===e.type){const r=10*e.face.side;n=[Bg(e,0,[-i,0],[i,0],r,0,.88*t),Bg(e,1,[i,0],[-i,0],-r,0,.88*t)]}else n="window"===e.type?[Bg(e,0,[-i,0],[i,0],-90,.27*t,.78*t),Bg(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[Bg(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(n.map(e=>Object.freeze(e)))}}({...e,face:o},r)});return{walls:a?.components.flatMap(e=>e.geom)||[],floor:l,openings:Object.freeze(h)}}}}_isoSceneKey(){if(!this._labsIso||"view"!==this._mode)return null;try{return this._isoSource()?.key??null}catch{return`${this._space}|invalid`}}_isoScene(){const e=this._isoSource();if(!e)return null;const t=this._isoGeometryCache.get(e.key);if(t)return{key:e.key,...t};const i=this._frameOf().rect,n=e.build(),r=ya(J_,this._cellCm),o=ya(10,this._cellCm),s=function(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)for(const e of o.leaves){const o=Math.hypot(e.closedVector[0],e.closedVector[1]);t=Math.min(t,e.hinge[0]-o),i=Math.min(i,e.hinge[1]-o),n=Math.max(n,e.hinge[0]+o),r=Math.max(r,e.hinge[1]+o)}return Hg([t,i,n,r])?{x:t,y:i,w:n-t,h:r-i}:null}(n.openings),a=ng({rect:s?gf(i,s):i,wallHeight:r,openingHeight:r,floorDepth:o}),l=function(e,t=Z_,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const n=[],r=[],o=[];let s=0;for(let a=0;a<(e||[]).length;a++){const l=e[a];for(let e=0;e<(l||[]).length;e++){const c=Fg(l[e],e>0);if(!(c.length<3||Math.abs(Pg(c))<1e-9)){n.push(Og(c,t,i)),s+=c.length;for(let n=0;ne.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:n.join(" "),sides:r,contactPath:o.join(" "),edgeCount:s}}(n.walls,Z_,r),c={geometry:l,floor:Eg(n.floor,o),openings:n.openings,frame:a};return gg(this._isoGeometryCache,e.key,c,8),{key:e.key,...c}}_effectiveProjection(){if("iso"!==this._desiredProjection||!this._model.length)return"flat";const e=this._isoSceneKey()||`${this._space}|invalid`;if(this._isoFallback.has(e))return"flat";try{const e=this._isoScene();return e?(t="iso",i=e.key,n=this._isoFallback,"iso"===t&&i&&!n.has(i)?"iso":"flat"):"flat"}catch(t){if(!this._isoFallback.has(e)){this._isoFallback.add(e);const i=e.split("|");console.warn(`HOUSEPLAN ISO FALLBACK: #89, space ${this._space}, fingerprint ${i[i.length-1]}, ${t instanceof Error?t.message:"renderer error"}`)}return"flat"}var t,i,n}_scenePoint(e){return"iso"===this._renderProjection?eg(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=tg([e.x,e.y]),i=tg([e.x+e.w,e.y+e.h]);return{x:t[0],y:t[1],w:i[0]-t[0],h:i[1]-t[1]}}_baseVb(){if("iso"===this._effectiveProjection()){if(!this._spaceDisplayForRender().showBorders){const e=ng({rect:this._frameOf().rect,wallHeight:ya(J_,this._cellCm)});return[e.x,e.y,e.w,e.h]}const e=this._isoScene()?.frame??this._frameOf().rect;return[e.x,e.y,e.w,e.h]}const e=this._frameOf().rect;return[e.x,e.y,e.w,e.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom()}_clearRoomFocus(e=!1){this._doubleFit.clear(),this._roomFocus=null,e&&(this._roomPointer=null)}_roomFitBounds(e,t){const i=Ht(e);if(!i||!e.id)return null;const n=this._spaceWalls,r=this._openCuts(),o=this._wallUnionGeometry(),s=n.length&&this._innerRoomContour(t,e.id,r,o?.roomGeom,o?.multiWallNodes)||i,a=this._spaceDisplayForRender();let l=[];if(a.showBorders&&n.length){const i=hs(t.rooms,e.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,_f);i&&(l=Es(i.poly,i.offsets,o?.multiWallNodes)||i.poly)}const c=this._effectiveProjection();return function(e){const t=e.floor,i=e.wall||[],n=e.padding??0;if(!t.length||!t.every(cg)||!i.every(cg)||!Number.isFinite(n)||n<0)return null;if("flat"===e.projection)return dg([...t,...i],n);const r=e.wallHeight??0,o=e.floorDepth??0;if(!Number.isFinite(r)||r<0||!Number.isFinite(o)||o<0)return null;try{const e=[];for(const i of t){const t=[i[0],i[1]];e.push(eg(t,0)),o>0&&e.push(eg(t,-o))}for(const t of i){const i=[t[0],t[1]];e.push(eg(i,0)),r>0&&e.push(eg(i,r))}return dg(e,n)}catch{return null}}({floor:s,wall:l,projection:c,wallHeight:"iso"===c&&a.showBorders?ya(J_,this._cellCm):0,floorDepth:"iso"===c&&a.showBorders?ya(10,this._cellCm):0,padding:a.showBorders?ya(1.25,this._cellCm):0})}_fitRoom(e,t=!0){if("view"!==this._mode)return!1;this._doubleFit.clear();const i=this._spaceModel(),n=i?.rooms.find(t=>t.id===e);if(!i||!n)return this._clearRoomFocus(),!1;const r=this._roomFitBounds(n,i);if(!r)return this._clearRoomFocus(),!1;this._roomFocus={spaceId:i.id,roomId:e};const o=this._stageEl;if(!o||o.clientWidth<=0||o.clientHeight<=0)return!0;const s=vi(this._baseVb(),o.clientWidth/o.clientHeight),a=function(e){const t=e.safeFraction??.8,{bounds:i,baseFit:n,stageWidth:r,stageHeight:o,minZoom:s,maxZoom:a}=e;if(!hg(i)||!hg(n)||!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||!Number.isFinite(s)||s<=0||!Number.isFinite(a)||a1)return null;const l=r/o,c=Math.max(i.w/t,i.h/t*l);if(!Number.isFinite(c)||c<=0)return null;const h=Math.min(a,Math.max(s,n.w/c)),d=n.w/h,u=d/l;return{zoom:h,viewBox:{x:i.x+i.w/2-d/2,y:i.y+i.h/2-u/2,w:d,h:u}}}({bounds:r,baseFit:s,stageWidth:o.clientWidth,stageHeight:o.clientHeight,minZoom:kf.ZOOM_MIN,maxZoom:kf.ZOOM_MAX});if(!a)return this._clearRoomFocus(),!1;const l=this._cameraState(),c=function(e,t,i,n){const r=[e,t,i,n],o=Math.min(...r.map(e=>e.x)),s=Math.min(...r.map(e=>e.y));return{x:o,y:s,w:Math.max(...r.map(e=>e.x+e.w))-o,h:Math.max(...r.map(e=>e.y+e.h))-s}}(s,l.viewBox,a.viewBox,r);return a.viewBox=this._clampView(a.viewBox,c),t?(this._prepareCameraCommand(),this._startCameraTransition(a,c,"room",220),!0):(this._cancelCameraTransition(!1),G_(l,a)||(this._zoom=a.zoom,this._view={...a.viewBox},this.requestUpdate()),!0)}_fitAll(e="fit"){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom(e)}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
+
`:V}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const e=this._serverCfg?.spaces||[];let t=e.length+":";for(const i of e){t+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const e of i.rooms||[]){const i=e.poly?.[0],n=e.poly?.[e.poly.length-1];t+=(e.poly?.length||0)+"."+(e.id||"")+"."+(e.open_to||[]).join("+")+"."+(e.area||"")+"."+JSON.stringify(e.settings||0)+"."+(e.x??"")+","+(e.y??"")+","+(e.w??"")+","+(e.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(n?n[0]+"/"+n[1]:"")+";"}}return t}get _model(){if(!this._serverCfg)return[];const e=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===e)return this._modelCache.model;const t=this._buildModel();return this._modelCache={key:e,model:t},t}_buildModel(){if(!this._serverCfg)return[];const e=this._renderCfg;return Da(e).map((t,i)=>{const n=e.spaces[i]?.plan_url;return t.bg&&n?{...t,bg:{...t.bg,href:n}}:t})}_spaceModel(){return this._hasFixedFloor?Bl(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Bl(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!0,this._clearRoomFocus(!0);for(const e of this._pointers.keys())for(const t of this.renderRoot.querySelectorAll("*"))try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._drag=null,this._deviceDrag=null,this._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary?.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=dd({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!t.length&&e.settings?.filter_seeded)return;e.markers=e.markers||[];const i=[];for(const n of t){const t="h"+n.slice(n.indexOf(":")+1);e.markers.push({id:t,binding:n,hidden:!0}),i.push(n.slice(n.indexOf(":")+1))}const n={...e.settings||{},filter_seeded:!0};delete n.show_all,i.length&&Array.isArray(n.new_device_ids)&&(n.new_device_ids=n.new_device_ids.filter(e=>!i.includes(e))),e.settings=n,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const e=this._settings.icon_rules;if(!e||!Array.isArray(e)||!e.length)return;const t=JSON.stringify(e);return t!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=t,this._rulesCompiled=ye(e)),this._rulesCompiled}get _fillColors(){return Xi(this._settings)}get _excluded(){return yd(this._settings)}_setAreaLifecycleConfig(e){this._cfgEpochPreservedConfig=e,this._serverCfg=e}willUpdate(e){if(e.has("_serverCfg")){const e=this._cfgEpochPreservedConfig===this._serverCfg;this._cfgEpochPreservedConfig=null,e||this._cfgEpoch++,this._renderLife.invalidate()}if(this._syncEmptySpaceState(),this._dangerConfirm&&(this._dangerConfirmMissingSpace()||"warm"===this._syncDangerConfirmLocaleGate())&&this._cancelDangerConfirm(),e.has("hass")&&this.hass){const t=this._visibleDeviceSnapshot||this._candidateDeviceSnapshot;this._renderLife.observe(e.get("hass"),this.hass,t?{entityIds:t.entityIds}:null,()=>A_(this))}this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._visualFrameFingerprint()),this._captureRenderDeviceSnapshot()}updated(){this._liveRt?.commit(),this._editorRuntime?._commitLiveEditor(),this._pruneDevicePressFeedback(),this._syncDayCycleClock(),this._warmSnapshot(),this._editorRuntime&&this._dtMeasure();const e=this._stageEl;e&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(e)),e&&this._booting&&!this._bootTimer&&this._bootWatch();const t=this.renderRoot.querySelector(".hdr");if(t&&e&&!this._roHdr){const i=()=>{const t=this.renderRoot.querySelector("ha-card");if(!t)return;const i=e.getBoundingClientRect().top-t.getBoundingClientRect().top,n=Math.min(Math.max(t.getBoundingClientRect().top,0),120),r=Math.round(i+n);r>=0&&r!==this._hdrH&&(this._hdrH=r),r>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:r,stageH:e.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(t),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(e&&!this._view&&this._refitView(),this._editorSecondary?.afterRender(),this._settleContinuityFrame(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const e=function(e){const t=e?.floors;if(!t||"object"!=typeof t)return[];const i=[];for(const e of Object.values(t))e&&e.floor_id&&i.push({id:e.floor_id,name:e.name||e.floor_id,level:e.level??null});return i.sort((e,t)=>{const i=e.level??1e9,n=t.level??1e9;return i!==n?i-n:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptConfigCapabilities(e){const t=e&&"object"==typeof e?e:{};this._haIntegrationVersion="string"==typeof t.integration_version?t.integration_version:this._haIntegrationVersion;const i=t.support_api;this._haSupportApi="number"==typeof i&&Number.isSafeInteger(i)?i:null,this._haDecorAssetsApi=1===t.decor_assets_api?1:null}async _syncDecorAssets(e){const t=++this._decorAssetSyncToken;if(1!==this._haDecorAssetsApi||!this.hass)return void(this._decorAssets=new Map);const i=await lm(this.hass,om(e),this._cfgRev);t===this._decorAssetSyncToken&&(this._decorAssets=i,this._resign(),this.requestUpdate())}_adoptStructuralResponses(e,t,i){const n=e?.config,r=n&&Array.isArray(n.spaces)?n:null,o=El(r),s=o!==(this._cfgContentFingerprint||El(this._serverCfg));if(s&&(this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._pendingPhysicalWrites.clear(),this._clearRoomFocus(!0),this._cancelCameraTransition(!1),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=r,this._seedDecorStyle(this._serverCfg),this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=Xc(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);Jc(t)!==Jc(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let a=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},n=El(e);a=n!==(this._layoutContentFingerprint||El(this._layout)),a&&(this._cancelCameraTransition(!1),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._layout=e,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._adoptConfigCapabilities(e),this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),s&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),a&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:s,layoutChanged:a}}_resumePendingNavMode(){if(!this._pendingNavMode||!this._canEdit||this._config?.kiosk)return!1;const e=this._pendingNavMode;return this._pendingNavMode=null,this._setMode(e,!1),!0}async _loadFromServer(){this._loading=!0,this._loadTries++;const e=this._space,t=!!this._view;try{const[i,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),r=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=El(r)!==(this._cfgContentFingerprint||El(this._serverCfg))||El(n?.layout??{})!==(this._layoutContentFingerprint||El(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(r)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0)}o&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),this._connectionWasLost=!1,this._serverStorage=!0,"boolean"==typeof i?.can_write&&(this._serverCanWrite=i.can_write),this._canOptimizeUndo=!(!i?.can_optimize_undo&&!n?.can_optimize_undo),this._adoptStructuralResponses(i,n),this._syncDecorAssets(r).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:t&&this._space===e||this._restoreZoom(),this._loadOk=!0,this.hass.callWS({type:"houseplan/trail/get"}).then(e=>{this._vacSrvTrails=e?.trails||{},this.requestUpdate()}).catch(()=>{}),this._ensureLiveSyncSubscriptions()}catch(e){if(this._serverCfg)this._scheduleLoadRetry(!0);else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(uf)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1,this._continuityDataReady=!0,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}}_ensureLiveSyncSubscriptions(){const e=this.hass?.connection;if(!e)return;if(e!==this._liveSyncConnection&&(this._unsubCfg?.(),this._unsubCfg=null,this._unsubLayout?.(),this._unsubLayout=null,this._unsubTrail?.(),this._unsubTrail=void 0,this._unsubVirtual?.(),this._unsubVirtual=null,this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=e),this._liveSyncAttempt)return;const t=this._liveSyncGeneration,i=[],n=(n,r,o,s)=>{n()||i.push(async()=>{const i=await e.subscribeEvents(s,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!n()?r(i):i?.()})};if(n(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),n(()=>this._unsubTrail,e=>{this._unsubTrail=e},"houseplan_trail_updated",async()=>{try{const e=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=e?.trails||{},this.requestUpdate()}catch{}}),n(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),n(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=th(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const r=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=r,r.finally(()=>{this._liveSyncAttempt===r&&(this._liveSyncAttempt=null)})}async _reloadConfigOnly(e=!1,t){if(!e){if(void 0!==t&&t<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,t),400))}this._beginContinuityCandidate("config-reload",!1);try{const e=await this.hass.callWS({type:"houseplan/config/get"}),t=e?.config&&Array.isArray(e.config.spaces)?e.config:null;if(El(t)!==(this._cfgContentFingerprint||El(this._serverCfg))&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0);const i=this._space;this._adoptStructuralResponses(e),this._syncDecorAssets(t).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._space!==i&&this._restoreZoom(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(e){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(e)}))}finally{this._continuityDataReady=!0,this.requestUpdate()}}_scheduleLoadRetry(e=!1){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,!e&&this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}async _ensureLiveRuntime(){if(this._liveRt)return;const e=await import("./live-interaction-runtime-C2JvTP_O.js");this._liveRt=new e.LiveRuntime(this),this.isConnected&&this.requestUpdate()}_liveVp(e=!1){this._liveRt?this._liveRt.viewport(e):this.requestUpdate()}_syncLiveHover(){this._liveRt?this._liveRt.hover():this.requestUpdate()}_flushHa(){this._liveRt?.take()&&(this._terminalFrame=2,super.requestUpdate())}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRev=-1,this._haBindingCacheKey="",this._renderLife.invalidate(),this._areaSnapshotCleanupCandidates.clear(),this._planHassMemo=null,this._haRegistryRelease=uh(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return mh(this.hass)}get _planHass(){const e=this._haRegistry,t=gh(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const n=vh(this.hass,e),r=yh(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:n,full:r},n}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRt.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+h_)>e?1:0)).join("|"),i=Jc(this._virtualLights);if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotLayout===this._layout&&this._capturedSnapshotConfigEpoch===this._cfgEpoch&&this._capturedSnapshotVirtual===i&&this._capturedSnapshotActivity===t)return;const n=this._planHass,r=new Map,o=new Map,s=new Set(["sun.sun"]);this._vacFit?.source&&s.add(this._vacFit.source);const a=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void s.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?a.add(n):"entity"===i&&s.add(n)};for(const e of this._model)for(const t of e.rooms)t.area&&l.add(t.area),c(t.settings?.temp_source),c(t.settings?.hum_source);for(const e of this._serverCfg?.spaces||[])for(const t of e.openings||[])for(const e of Kt(t))s.add(e);for(const e of this._serverCfg?.spaces||[])for(const t of e.decor||[])if("text"===t.kind){for(const e of String(t.text||"").matchAll(/\{([^{}\r\n]+)\}/g)){const t=Di(e[1]);t?.entity&&s.add(t.entity)}t.entity&&s.add(t.entity),o.set(`decor:${e.id}:${t.id}`,Oi(t.text,t,n,e=>!!n.entities?.[e]&&!!n.states?.[e]&&!cd(n,e,ld(this._markers))))}const h=Kh(n,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])r.set(_p(e.id,t),dp(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:h,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}));if(this._isVacDev(e)){const t=this._vacSource(e,n),i=t?ec(n?.states?.[t]?.attributes):null,r=this._vacRt.get(e.id),s=vc(e.id,e.marker?.vacuum??null,e.space,t),a=bc({routes:s,observed:wc(s,[t],t=>this._vacObservedMapId(e,t,n)),spaceIds:new Set(this._model.map(e=>e.id))});o.set(`vacuum:${e.id}`,{source:t,telemetry:i,routes:s,resolution:a,mapId:i?this._vacMapId(e,i,n):null,runtime:r?{trail:r.trail,lastTs:r.lastTs,moving:r.moving,jump:r.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=mp({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,positions:(u=this._model.length>0,p=this._devices,m=e=>this._livePos(e),u?new Map(p.map(e=>[e.id,m(e)])):new Map),facts:o,entityIds:s,deviceIds:a,areaIds:l});var u,p,m;this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotLayout=this._layout,this._capturedSnapshotActivity=t,this._capturedSnapshotConfigEpoch=this._cfgEpoch,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=d:(this._visibleDeviceSnapshot=d,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}get _renderPlanHass(){return this._renderDeviceSnapshot?.hass||this._planHass}get _renderDevices(){return this._renderDeviceSnapshot?.devices||this._devices}get _renderVacuumDevices(){return this._renderDeviceSnapshot?.vacuumDevices||this._devices}get _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return kh(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const{registry:e,bindings:t}=this._renderLife.diagnostics(this.hass,this._markers,e=>this._bindingStatus(e));return{registry:{...e,lastSuccessAgeMs:e.lastSuccess?Math.max(0,Date.now()-e.lastSuccess):null},bindings:t}}_openBindingInHa(e){const[t,i]=e.split(":");if(i)if("device"!==t){if("entity"===t){const e=this._fullRegistryHass.entities?.[i];e?.device_id&&kf("/config/devices/device/"+encodeURIComponent(e.device_id))}}else kf("/config/devices/device/"+encodeURIComponent(i))}_bindingHasHaPage(e){const[t,i]=e.split(":");return!!i&&("device"===t||"entity"===t&&!!this._fullRegistryHass.entities?.[i]?.device_id)}_toggleMarkerDialogVisibility(){const e=this._markerDialog;if(!e)return;const t="ha"===e.bindingMode?this._bindingStatus(e.binding):null,i=e.hideFromPlan||"ha_disabled"===t?.kind;i&&"ha_disabled"===t?.kind?this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device")):i&&"unverified"===t?.kind?this._showToast(this._t("toast.ha_binding_unverified")):this._markerDialog={...e,hideFromPlan:!i}}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}_display(e){return this._signer.display(this.hass,e)}_referencedContentUrls(){const e=fn(this._serverCfg);for(const t of this._decorAssets.values())e.add(t.url);return e}_resign(){this._signer.resign(this.hass,this._referencedContentUrls())}_onLayoutEvent(e){e<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{e<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(e){const t=e?.rev;"number"==typeof t&&t>this._layoutRev&&(this._layoutRev=t)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;this._beginContinuityCandidate("layout-reload",!1);const e=new Map;for(const t of this._dirtyPos)this._layout[t]&&e.set(t,this._layout[t]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[t,i]of this._sentPos)e.set(t,i);try{const t=await this.hass.callWS({type:"houseplan/layout/get"}),i={...t?.layout||{}};for(const[t,n]of e)null===n?delete i[t]:i[t]=n;const n=El(i);n!==El(this._layout)&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear(),this._layout=i),this._layoutContentFingerprint=n,this._layoutRev=t?.rev??this._layoutRev,this._canOptimizeUndo=!!t?.can_optimize_undo,this._haIntegrationVersion="string"==typeof t?.integration_version?t.integration_version:this._haIntegrationVersion,this._undoKind=t?.undo_kind||null,this._cacheSnapshot(),this.requestUpdate()}catch{}finally{this._continuityDataReady=!0,this.requestUpdate()}}_persistLocalLayout(){this._layout=eo(Hr(this._layout)),localStorage.setItem(uf,JSON.stringify(this._layout))}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=gh(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+xu(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const n=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=pd({hass:e,registry:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:e=>this._t(e),iconRules:this._iconRules});const r=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+r.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const n of r)i.set(n,kh(e,n,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=wh(),i=Date.now();for(const[n,r]of e)"active"===r.kind?t[n]={kind:"active",ts:i}:"ha_disabled"===r.kind&&(t[n]={kind:"ha_disabled",reason:r.reason,ts:i});const n=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));sh=n;try{localStorage.setItem(oh,JSON.stringify(n))}catch{}}(i),this._haBindingCacheKey=o}let s=null;if(t.authoritative&&(s=this._resolveAreaRelocations(t),this._areaRelocationIds=new Set(s.relocateIds),this._areaRelocationIds.size)){this._cancelDeviceDrag();const e=this._areaRelocationIds;this._devicePositionHistory.removeWhere(({before:t,after:i})=>e.has(t.deviceId)||e.has(i.deviceId));const t=this._markerDialog;if(t?.devId&&!t.roomTouched&&this._areaRelocationIds.has(t.devId)){const e=this._devices.find(e=>e.id===t.devId);e&&(this._markerDialog={...t,room:e.marker?.room_id?`${e.space}#@${e.marker.room_id}`:e.space&&e.area?`${e.space}#${e.area}`:""})}}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),s&&this._syncAreaRelocations(s),this._syncActivityRuntime();const a=new Set(this._devices.filter(e=>!e.hidden).map(e=>e.id));for(const e of this._vacRt.keys())a.has(e)||this._vacRt.delete(e);if(this._infoCard){const e=this._devices.find(e=>e.id===this._infoCard.id);this._infoCard=e&&"ha_disabled"!==e.bindingStatus?.kind?e:null}const l=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==n.get(e.id));l&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._deviceDrag&&"ha_disabled"===this._devices.find(e=>e.id===this._deviceDrag.id)?.bindingStatus?.kind&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear()),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(wf(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_resolveAreaRelocations(e=this._haRegistry){const t=function(e){const t=Yp(e.snapshot),i=new Set(Object.values(t).map(e=>e.binding)),n=new Map;for(const[t,r]of e.previousCandidates||[])Kp(t)&&Number.isFinite(r)&&i.has(t)&&n.set(t,r);const r=new Set,o=new Set;for(const t of e.markers||[])t.removed||(Gp(t.id)&&r.add(t.id),Kp(t.binding)&&o.add(t.binding));const s=new Set(Object.keys(e.registryDevices||{})),a=new Set(Object.keys(e.registryEntities||{})),l=new Set(Object.keys(e.liveStates||{})),c=new Map;for(const[e,i]of Object.entries(t)){const t=c.get(i.binding)||[];t.push(e),c.set(i.binding,t)}const h=new Set;let d=!1;for(const[t,i]of c){const[c,u]=em(t);if(o.has(t)||i.some(e=>r.has(e))||("device"===c?s.has(u):a.has(u)||l.has(u))){n.delete(t);continue}const p="device"===c?s.size>0:a.size>0;if(!e.authoritative||!p)continue;const m=n.get(t);if(void 0!==m){if(m!==e.revision)for(const e of i)h.add(e)}else n.set(t,e.revision),d=!0}return{removeIds:h,candidates:n,needsConfirmationRefresh:d}}({snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,revision:e.revision,registryDevices:e.devices,registryEntities:e.entities,liveStates:this.hass?.states,markers:this._markers,previousCandidates:this._areaSnapshotCleanupCandidates});return this._areaSnapshotCleanupCandidates=t.candidates,t.needsConfirmationRefresh&&this._canEdit&&ph(this.hass),tm({devices:this._devices,model:this._model,layout:this._layout,snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,cleanupSnapshotIds:t.removeIds,coordinateScale:yf})}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const e=this._devices.filter(e=>!e.marker&&!e.virtual).map(e=>e.id).sort(),t=e.join(",");if(t===this._newSyncKey)return;this._newSyncKey=t;const i=this._settings,{fresh:n,known:r}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),n=e.filter(e=>!i.has(e));return{fresh:n,known:n.length?[...t,...n]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||n.length){const e=[...new Set([...i.new_device_ids||[],...n])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:r,new_device_ids:e}},this._saveConfig()}}_syncAreaRelocations(e){if(!(this._serverCfg&&this._norm&&this._canEdit&&this._haRegistry.authoritative))return;const t=e.decisions.filter(e=>e.updateSnapshot||e.removeSnapshot);if(!t.length)return;const i=El({snapshot:Yp(this._settings.marker_area_snapshot),decisions:t});i!==this._areaRelocationSyncKey&&(this._areaRelocationSyncKey=i,this._areaRelocationWrite=this._areaRelocationWrite.catch(()=>{}).then(async()=>{if(!this._serverCfg||!this._haRegistry.authoritative)return;const e=this._resolveAreaRelocations();this._areaRelocationIds=new Set(e.relocateIds);const t=new Set,i=new Map;let n=!1;for(const r of e.decisions){if(!r.relocate)continue;const e=T_(this._layout,r.id);try{await this._persistDevicePlacement(r.id,null),t.add(r.id),e&&i.set(r.id,e),this._areaRelocationIds.delete(r.id)}catch(t){n=!0,this._layout=D_(this._layout,r.id,e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}}const r=this._settings,o=function(e,t,i=new Set){const n=Yp(e);for(const e of t.decisions)e.removeSnapshot?delete n[e.id]:e.updateSnapshot&&e.binding&&e.area&&(e.relocate&&!i.has(e.id)||(n[e.id]={binding:e.binding,area:e.area}));return n}(r.marker_area_snapshot,e,t),s=[...new Set([...Array.isArray(r.new_device_ids)?r.new_device_ids:[],...t])],a=El(o)!==El(Yp(r.marker_area_snapshot)),l=El(s)!==El(Array.isArray(r.new_device_ids)?r.new_device_ids:[]);if(a||l){const e=r.marker_area_snapshot,n=r.new_device_ids,a={...this._serverCfg,settings:{...r,marker_area_snapshot:o,...l?{new_device_ids:s}:{}}};this._setAreaLifecycleConfig(a);try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.cancel(),await this._writeConfig()}catch(r){const a=this._settings;if(El(a.marker_area_snapshot)===El(o)&&El(a.new_device_ids)===El(l?s:n)){const t={...a};void 0===e?delete t.marker_area_snapshot:t.marker_area_snapshot=e,void 0===n?delete t.new_device_ids:t.new_device_ids=n;const i={...this._serverCfg,settings:t};this._setAreaLifecycleConfig(i),this._cfgContentFingerprint=El(this._serverCfg)}for(const e of t)this._areaRelocationIds.add(e);const c=new Set;for(const[e,t]of i)try{await this._persistDevicePlacement(e,t)}catch(t){c.add(e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}this._areaRelocationSyncKey="",this._regSignature="";if("conflict"===(r&&"object"==typeof r&&"code"in r?r.code:void 0)?(this._showToast(this._t("toast.conflict")),await this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(r)})),c.size&&this._serverCfg){const e=this._settings,t=[...new Set([...Array.isArray(e.new_device_ids)?e.new_device_ids:[],...c])],i={...this._serverCfg,settings:{...e,new_device_ids:t}};this._setAreaLifecycleConfig(i);try{await this._writeConfig()}catch(e){this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)}))}}}}n&&(this._areaRelocationSyncKey=""),this.requestUpdate()}))}get _newIds(){const e=this._settings.new_device_ids;return new Set([...Array.isArray(e)?e:[],...this._areaRelocationIds])}_ackNewDevice(e){if(!this._newIds.has(e)||!this._serverCfg)return;const t=this._settings;this._serverCfg={...this._serverCfg,settings:{...t,new_device_ids:(t.new_device_ids||[]).filter(t=>t!==e)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(e){if(!e)return null;const t=[];for(const i of this._renderDevices){if(i.area!==e||i.virtual)continue;const n=Zh(this._renderPlanHass,i.entities);null!=n&&t.push(n)}return fi(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}_defaultPositions(){const e={},t=this._config?.icon_size??2.5;for(const i of this._model){const n=t/100*il(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const r=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!r.length)continue;const o=this._roomBounds(t),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(r.length*a/Math.max(l,1)))),h=Math.ceil(r.length/c),d=a/c,u=l/Math.max(h,1),p=r.map((e,t)=>({x:o.x+s+d*(t%c+.5),y:o.y+s+u*(Math.floor(t/c)+.5)}));yi(p,o,n,.5*s),r.forEach((t,i)=>e[t.id]=Ea(p[i]))}}return e}_pos(e){const t=this._renderDeviceSnapshot?.positions.get(e.id);return t?{x:t.x,y:t.y}:this._livePos(e)}_livePos(e){const t=this._spaceModelById(e.space),i=this._areaRelocationIds.has(e.id)?void 0:this._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*yf,y:i.y*yf}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Ea(tl(t)):{x:500,y:500}}_devicePlacementForCanvas(e,t,i){if(!this._norm)return{x:Math.round(t),y:Math.round(i)};const n=this._gridPitch,r=Math.round(t/n)*n,o=Math.round(i/n)*n;return{s:e.space,x:Ha(r/yf),y:Ha(o/yf)}}_previewDevicePlacement(e,t){this._layout=D_(this._layout,e,t),this.requestUpdate()}_cancelDeviceDrag(){const e=this._deviceDrag;if(!e)return!1;this._editorRuntime?._cancelPointerMove("device"),this._deviceDrag=null;try{e.source?.hasPointerCapture?.(e.pointerId)&&e.source.releasePointerCapture(e.pointerId)}catch{}return this._previewDevicePlacement(e.id,e.before),!0}async _persistDevicePlacement(e,t){if(this._layout=D_(this._layout,e,t),this._serverStorage){let i=null,n=!1;try{let r;if(null===t)i=null,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/delete",device_id:e});else{const t=Qr(this._layout[e]);El(t)!==El(this._layout[e])&&(this._layout={...this._layout,[e]:t}),i=t,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t})}this._noteLayoutRev(r),this._layoutContentFingerprint=El(this._layout),this._cacheSnapshot()}finally{n&&this._sentPos.get(e)===i&&this._sentPos.delete(e)}return}this._persistLocalLayout()}_devicePositionStateValid(e){const t=this._devices.find(t=>t.id===e.deviceId);return!!t&&t.space===e.spaceId&&"ha_disabled"!==t.bindingStatus?.kind&&!!this._spaceModelById(e.spaceId)}async _runDevicePositionHistory(e){if(this._cancelDeviceDrag()||this._devicePositionBusy)return;const t="undo"===e?this._devicePositionHistory.undo():this._devicePositionHistory.redo();if(!t)return;const i="undo"===e?t.before:t.after;if(!this._devicePositionStateValid(i))return this._devicePositionHistory.clear(),this._showToast(this._t("history.device_stale")),void this.requestUpdate();const n=T_(this._layout,i.deviceId);i.spaceId!==this._space&&(this._commitSpace(i.spaceId),this._restoreZoom()),this._devicePositionBusy=!0,this._previewDevicePlacement(i.deviceId,i.placement);try{await this._persistDevicePlacement(i.deviceId,i.placement),this._showToast(this._t("undo"===e?"history.undone":"history.redone",{name:t.name}))}catch(t){this._previewDevicePlacement(i.deviceId,n),"undo"===e?this._devicePositionHistory.redo():this._devicePositionHistory.undo(),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}finally{this._devicePositionBusy=!1,this.requestUpdate()}}_undoDevicePosition(){this._runDevicePositionHistory("undo")}_redoDevicePosition(){this._runDevicePositionHistory("redo")}_savePos(e,t,i){this._spaceModelById(e.space)&&(this._layout=D_(this._layout,e.id,this._devicePlacementForCanvas(e,t,i)),this._dirtyPos.add(e.id),this._persistLayout())}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const n=i?null:this._renderDeviceSnapshot?.presentations.get(_p(e.id,t));return n||dp(this._renderPlanHass,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,designPreview:i,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._renderDevices,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion})}_deviceVisual(e){return this._devicePresentation(e).visual}_stateClass(e,t=this._deviceVisual(e)){const i=this._devicePresentation(e);return i.effectiveHidden?"":("icon_ripple"!==i.display||!1===this._config?.live_states||"alarm"===t.status||t.activity,hp({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=Qh(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:Jh(this._renderPlanHass,e.entities)}_bindingEntities(e){if("virtual"===e||!e)return[];const t=this._bindingStatus(e);return"active"===t.kind?t.enabledEntityIds:t.allEntityIds}_bindingHasClimate(e){return this._bindingEntities(e).some(e=>e.startsWith("climate."))}_bindingHasAlarm(e){return this._bindingEntities(e).some(e=>{const t=this._planHass?.states?.[e]||this.hass?.states?.[e],i=this._fullRegistryHass.entities[e]||this.hass?.entities?.[e];return Rn(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_deviceBindingActive(e,t=!0){if(e.virtual||"virtual"===e.bindingKind)return!0;if(!e.bindingKind||!e.bindingRef)return!1;const i=this._bindingStatus(`${e.bindingKind}:${e.bindingRef}`);return"active"===i.kind||(t&&this._showToast(this._t("ha_disabled"===i.kind?"toast.ha_disabled_action":"toast.ha_binding_unverified")),!1)}_openMoreInfo(e){e?this._planEntityAvailable(e)?(this._nativeMoreInfoEntity=e,wf(this,"hass-more-info",{entityId:e})):this._showToast(this._t("toast.ha_disabled_action")):this._showToast(this._t("toast.no_entity"))}_interruptViewGesture(e,t){if(clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),void 0!==e)for(const i of[t,this._stageEl])try{i?.hasPointerCapture?.(e)&&i.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._roomPointer=null,this._doubleFit.clear()}_closeInfoCard(){this._interruptViewGesture(),this._holdFired=!1,this._infoCard=null}_ctxDevice(e,t){"view"===this._mode&&(e.preventDefault(),e.stopPropagation(),this._deviceBindingActive(t)&&(t.primary?this._openMoreInfo(t.primary):this._infoCard=t))}_clickDevice(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if(e.stopPropagation(),this._deviceDrag?.moved||this._suppressClick||this._holdFired)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const n=Ld(i.tapAction,i.primary?.split(".")[0]);if("none"===n)return;const r=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===n){const e=this._toggleIntent(i);if(!e)return;if(!Vd(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=Vd(e);if(!t)return;if("virtual-light"===t.kind)return this._startDevicePressFeedback(i.id),void this.hass.callWS({type:"houseplan/virtual_light/toggle",marker_id:t.markerId}).then(e=>{const t=th(this._virtualLights,e);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}).catch(e=>this._showToast(this._t("toast.virtual_light_toggle_failed",{err:this._errText(e)})));const{command:n}=t;this._startDevicePressFeedback(i.id),this.hass.callService(n.domain,n.service,n.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},n=((o=e).targets.length?o.targets:o.skippedTargets).map(e=>e.name||e.entityId||e.ref).join(", ")||i.name;if(i.marker?.tap_confirm){const r=this._toggleConfirmationLines(e);if(!r.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:n}),lines:r,initialIntent:e,deviceId:i.id,exec:()=>{const n=this._devices.find(e=>e.id===i.id),r=n?this._toggleIntent(n):null;r&&!Vd(r)&&this._showUnavailableToggleTargets(r)||(r&&Kd(e,r)?t(r):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===n)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===n){const e=i.marker?.tap_target||"",t=function(e){const t=String(e||"").split(".")[0];return"automation"===t?{domain:"automation",service:"trigger"}:"script"===t?{domain:"script",service:"turn_on"}:"scene"===t?{domain:"scene",service:"turn_on"}:null}(e),n=this.hass.states[e];if(!t||!n)return void this._showToast(this._t("toast.run_target_missing"));const o=n.attributes?.friendly_name||e;return void r(this._t("confirm.tap_run",{name:o}),()=>{this._deviceBindingActive(i)&&this._planEntityAvailable(e)&&(this._startDevicePressFeedback(i.id),this.hass.callService(t.domain,t.service,{entity_id:e}).then(()=>{this._stampActivity(i.id,"event",this._activitySourceKey(i)),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)}))))})}"more-info"===n&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||Vd(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Gd.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const n of e.skippedTargets){if(!Gd.has(n.reason))continue;const e=String(n.name||n.entityId||n.ref||"").trim();e&&!i.has(e)&&(i.add(e),t.push(e))}return t}(e);return!!t.length&&(this._showToast(1===t.length?this._t("toast.toggle_target_unavailable",{name:t[0]}):this._t("toast.toggle_targets_unavailable",{names:t.join(", ")})),!0)}_keyDevice(e,t){"Enter"!==e.key&&" "!==e.key||"view"!==this._mode&&"devices"!==this._mode||(e.preventDefault(),this._clickDevice(e,t))}_startDevicePressFeedback(e){const t=[...this.renderRoot.querySelectorAll(".dev[data-id]")].find(t=>t.dataset.id===e),i=t?.querySelector(".device-shell");if(!i||"function"!=typeof i.animate)return;const n=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),r=Number.isFinite(n)?Math.max(.95,Math.min(1,n)):1;this._devicePressAnimations.get(e)?.cancel();const o=this._reducedMotion?[{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"},{outlineColor:"var(--hp-accent, #3ea6ff)",outlineStyle:"solid",outlineWidth:"2px",outlineOffset:"2px",offset:.5},{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"}]:[{scale:String(r)},{scale:"0.95",offset:.5},{scale:"1"}],s=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,s);const a=()=>{this._devicePressAnimations.get(e)===s&&this._devicePressAnimations.delete(e)};s.addEventListener("finish",a,{once:!0}),s.addEventListener("cancel",a,{once:!0})}_cancelDevicePressFeedback(){for(const e of this._devicePressAnimations.values())e.cancel();this._devicePressAnimations.clear()}_pruneDevicePressFeedback(){if(!this._devicePressAnimations.size)return;const e=new Set([...this.renderRoot.querySelectorAll(".dev[data-id]")].map(e=>e.dataset.id||""));for(const[t,i]of this._devicePressAnimations)e.has(t)||(i.cancel(),this._devicePressAnimations.delete(t))}_t(e,t){return Su(xu(this.hass,this._config?.language),e,t)}get _colorPickerLabels(){return{title:this._t("color_picker.title"),hue:this._t("color_picker.hue"),saturation:this._t("color_picker.saturation"),value:this._t("color_picker.value"),hex:this._t("color_picker.hex"),invalidHex:this._t("color_picker.invalid_hex")}}_help(e){return this._editorRuntimeOrThrow()._help(e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(e){const t=[];for(const i of this._devices){if(i.space!==e.id||i.hidden)continue;const n=this._pos(i);t.push({minX:n.x,minY:n.y,maxX:n.x,maxY:n.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,n=Math.cos(i)*e.rlen/2,r=Math.sin(i)*e.rlen/2,o=Wa([[e.rx-n,e.ry-r],[e.rx+n,e.ry+r]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){if("image"===e.kind&&!this._decorAssets.has(e.asset_id)&&"decor"!==this._mode)continue;const n=Wa("line"===e.kind?[[e.x1*yf,e.y1*i],[e.x2*yf,e.y2*i]]:"text"===e.kind?[[e.x*yf,e.y*i]]:mr({x:e.x*yf,y:e.y*i,w:e.w*yf,h:e.h*i,angle:e.angle}));n&&t.push(n)}for(const i of this._physicalBodiesR(e)){const e=Wa(i);e&&t.push(e)}}return Ka(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:yf,h:yf};return{rect:e,all:e,outliers:0}}const t=this._frame,i="view"!==this._mode;if(t&&t.id===e.id&&this._bdDrag)return t;if(t&&t.id===e.id&&t.model===e&&t.layout===this._layout&&t.devs===this._devices&&t.far===this._showFar&&t.grow===i)return t;const n=Ja(this._contentItems(e));let r=n.all||el(e),o=this._showFar?r:n.core||el(e);return t&&t.id===e.id&&i&&t.grow&&(o=bf(t.rect,o),r=bf(t.all,r)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:r,outliers:n.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),n=this._openingsR.map((e,t)=>({id:String(e.id||t),sourceIndex:t,type:e.type,x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen>0?e.rlen:900,flipH:!!e.flip_h,flipV:!!e.flip_v})),r=ba(ig,this._cellCm),o=ba(10,this._cellCm),s=`${e.id}|${function(e){let t=2166136261;const i=e=>{if("number"!=typeof e)if("string"!=typeof e)if("boolean"!=typeof e)if(Array.isArray(e)){t=qg(t,e.length);for(const t of e)i(t)}else{if(e&&"object"==typeof e){const t=e;for(const e of Object.keys(t).sort())i(e),i(t[e]);return}t=qg(t,-1)}else t=qg(t,e?1:0);else for(const i of e)t=qg(t,i.codePointAt(0)||0);else t=qg(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:n,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:tg,wallHeight:r,floorEdgeHeight:o,algorithm:3})}`;return{key:s,build:()=>{const o=sa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,s=this._roomWallOpeningInputs(this._openingsR,e),a=t.length||o.length?Ps(e.rooms,t,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,yf,o):null;if(a&&("failed-core"===a.status||"not-applicable"===a.status))throw new Error("wall boolean geometry failed");const l=a?.paperGeom??Ts(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf);if(!l)throw new Error("floor boolean geometry failed");const c=a?.openingIndex||Hs(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf),h=n.map(e=>{const i=this._openingsR[e.sourceIndex],n="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?nu(i.partitionHost,n):t.length||"gate"===e.type?Ws(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:n}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(Wg([e.x,e.y,e.angle,e.length,e.face.ox,e.face.oy,t])&&e.length>0&&t>0))throw new Error("invalid isometric opening input");const i=e.length/2;let n;if("passage"===e.type)n=[];else if("gate"===e.type){const r=10*e.face.side;n=[Ug(e,0,[-i,0],[i,0],r,0,.88*t),Ug(e,1,[i,0],[-i,0],-r,0,.88*t)]}else n="window"===e.type?[Ug(e,0,[-i,0],[i,0],-90,.27*t,.78*t),Ug(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[Ug(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(n.map(e=>Object.freeze(e)))}}({...e,face:o},r)});return{walls:a?.components.flatMap(e=>e.geom)||[],floor:l,openings:Object.freeze(h)}}}}_isoSceneKey(){if(!this._labsIso||"view"!==this._mode)return null;try{return this._isoSource()?.key??null}catch{return`${this._space}|invalid`}}_isoScene(){const e=this._isoSource();if(!e)return null;const t=this._isoGeometryCache.get(e.key);if(t)return{key:e.key,...t};const i=this._frameOf().rect,n=e.build(),r=ba(ig,this._cellCm),o=ba(10,this._cellCm),s=function(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)for(const e of o.leaves){const o=Math.hypot(e.closedVector[0],e.closedVector[1]);t=Math.min(t,e.hinge[0]-o),i=Math.min(i,e.hinge[1]-o),n=Math.max(n,e.hinge[0]+o),r=Math.max(r,e.hinge[1]+o)}return Wg([t,i,n,r])?{x:t,y:i,w:n-t,h:r-i}:null}(n.openings),a=ag({rect:s?bf(i,s):i,wallHeight:r,openingHeight:r,floorDepth:o}),l=function(e,t=tg,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const n=[],r=[],o=[];let s=0;for(let a=0;a<(e||[]).length;a++){const l=e[a];for(let e=0;e<(l||[]).length;e++){const c=Ng(l[e],e>0);if(!(c.length<3||Math.abs(Eg(c))<1e-9)){n.push(Hg(c,t,i)),s+=c.length;for(let n=0;ne.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:n.join(" "),sides:r,contactPath:o.join(" "),edgeCount:s}}(n.walls,tg,r),c={geometry:l,floor:Bg(n.floor,o),openings:n.openings,frame:a};return bg(this._isoGeometryCache,e.key,c,8),{key:e.key,...c}}_effectiveProjection(){if("iso"!==this._desiredProjection||!this._model.length)return"flat";const e=this._isoSceneKey()||`${this._space}|invalid`;if(this._isoFallback.has(e))return"flat";try{const e=this._isoScene();return e?(t="iso",i=e.key,n=this._isoFallback,"iso"===t&&i&&!n.has(i)?"iso":"flat"):"flat"}catch(t){if(!this._isoFallback.has(e)){this._isoFallback.add(e);const i=e.split("|");console.warn(`HOUSEPLAN ISO FALLBACK: #89, space ${this._space}, fingerprint ${i[i.length-1]}, ${t instanceof Error?t.message:"renderer error"}`)}return"flat"}var t,i,n}_scenePoint(e){return"iso"===this._renderProjection?rg(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=og([e.x,e.y]),i=og([e.x+e.w,e.y+e.h]);return{x:t[0],y:t[1],w:i[0]-t[0],h:i[1]-t[1]}}_baseVb(){if("iso"===this._effectiveProjection()){if(!this._spaceDisplayForRender().showBorders){const e=ag({rect:this._frameOf().rect,wallHeight:ba(ig,this._cellCm)});return[e.x,e.y,e.w,e.h]}const e=this._isoScene()?.frame??this._frameOf().rect;return[e.x,e.y,e.w,e.h]}const e=this._frameOf().rect;return[e.x,e.y,e.w,e.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom()}_clearRoomFocus(e=!1){this._doubleFit.clear(),this._roomFocus=null,e&&(this._roomPointer=null)}_roomFitBounds(e,t){const i=Ht(e);if(!i||!e.id)return null;const n=this._spaceWalls,r=this._openCuts(),o=this._wallUnionGeometry(),s=n.length&&this._innerRoomContour(t,e.id,r,o?.roomGeom,o?.multiWallNodes)||i,a=this._spaceDisplayForRender();let l=[];if(a.showBorders&&n.length){const i=hs(t.rooms,e.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,yf);i&&(l=Ns(i.poly,i.offsets,o?.multiWallNodes)||i.poly)}const c=this._effectiveProjection();return function(e){const t=e.floor,i=e.wall||[],n=e.padding??0;if(!t.length||!t.every(pg)||!i.every(pg)||!Number.isFinite(n)||n<0)return null;if("flat"===e.projection)return _g([...t,...i],n);const r=e.wallHeight??0,o=e.floorDepth??0;if(!Number.isFinite(r)||r<0||!Number.isFinite(o)||o<0)return null;try{const e=[];for(const i of t){const t=[i[0],i[1]];e.push(rg(t,0)),o>0&&e.push(rg(t,-o))}for(const t of i){const i=[t[0],t[1]];e.push(rg(i,0)),r>0&&e.push(rg(i,r))}return _g(e,n)}catch{return null}}({floor:s,wall:l,projection:c,wallHeight:"iso"===c&&a.showBorders?ba(ig,this._cellCm):0,floorDepth:"iso"===c&&a.showBorders?ba(10,this._cellCm):0,padding:a.showBorders?ba(1.25,this._cellCm):0})}_fitRoom(e,t=!0){if("view"!==this._mode)return!1;this._doubleFit.clear();const i=this._spaceModel(),n=i?.rooms.find(t=>t.id===e);if(!i||!n)return this._clearRoomFocus(),!1;const r=this._roomFitBounds(n,i);if(!r)return this._clearRoomFocus(),!1;this._roomFocus={spaceId:i.id,roomId:e};const o=this._stageEl;if(!o||o.clientWidth<=0||o.clientHeight<=0)return!0;const s=vi(this._baseVb(),o.clientWidth/o.clientHeight),a=function(e){const t=e.safeFraction??.8,{bounds:i,baseFit:n,stageWidth:r,stageHeight:o,minZoom:s,maxZoom:a}=e;if(!mg(i)||!mg(n)||!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||!Number.isFinite(s)||s<=0||!Number.isFinite(a)||a1)return null;const l=r/o,c=Math.max(i.w/t,i.h/t*l);if(!Number.isFinite(c)||c<=0)return null;const h=Math.min(a,Math.max(s,n.w/c)),d=n.w/h,u=d/l;return{zoom:h,viewBox:{x:i.x+i.w/2-d/2,y:i.y+i.h/2-u/2,w:d,h:u}}}({bounds:r,baseFit:s,stageWidth:o.clientWidth,stageHeight:o.clientHeight,minZoom:Mf.ZOOM_MIN,maxZoom:Mf.ZOOM_MAX});if(!a)return this._clearRoomFocus(),!1;const l=this._cameraState(),c=function(e,t,i,n){const r=[e,t,i,n],o=Math.min(...r.map(e=>e.x)),s=Math.min(...r.map(e=>e.y));return{x:o,y:s,w:Math.max(...r.map(e=>e.x+e.w))-o,h:Math.max(...r.map(e=>e.y+e.h))-s}}(s,l.viewBox,a.viewBox,r);return a.viewBox=this._clampView(a.viewBox,c),t?(this._prepareCameraCommand(),this._startCameraTransition(a,c,"room",220),!0):(this._cancelCameraTransition(!1),Z_(l,a)||(this._zoom=a.zoom,this._view={...a.viewBox},this.requestUpdate()),!0)}_fitAll(e="fit"){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom(e)}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -4956,89 +4956,89 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef style="left:${a.toFixed(1)}%;top:${l.toFixed(1)}%" @click=${e=>{e.stopPropagation(),this._fitAll("home")}}> - `}_stageAspect(){const e=this._stageEl,t=this._baseVb();return e&&e.clientHeight?e.clientWidth/e.clientHeight:t[2]/t[3]}_viewOr(e){return this._view&&this._view.w?this._view:vi(e,this._stageAspect())}_roomLabelReferenceViewWidth(e){if(!this._markup)return e.w;const t=this._stageEl,i=this.renderRoot.querySelector(".editorchrome");if(!t||t.clientWidth<=0||t.clientHeight<=0)return e.w;const n=t.clientHeight+(i?.getBoundingClientRect().height||0);return n<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,n).w}_screenToVb(e,t){const i=this._stageEl,n=this._viewOr(this._baseVb()),r=i?.clientWidth||1,o=i?.clientHeight||1;return[n.x+e/r*n.w,n.y+t/o*n.h]}_clampView(e,t){const i=(e,t,i,n)=>{const r=1*Math.max(t,n),o=i-r,s=i+n-t+r;return Math.max(Math.min(o,s),Math.min(Math.max(o,s),e))};return{w:e.w,h:e.h,x:i(e.x,e.w,t.x,t.w),y:i(e.y,e.h,t.y,t.h)}}_applyView(e,t,i){this._cancelCameraTransition(!1);const n=this._baseVb(),r=vi(n,this._stageAspect()),o=Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,e)),s=r.w/o,a=r.h/o,l=this._viewOr(n),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-s/2,y:h-a/2,w:s,h:a},r);return!(this._view&&Math.abs(this._zoom-o)<1e-9&&Math.abs(this._view.x-d.x)<1e-6&&Math.abs(this._view.y-d.y)<1e-6&&Math.abs(this._view.w-d.w)<1e-6&&Math.abs(this._view.h-d.h)<1e-6)&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=o,this._view=d,!0)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const e=()=>{if(!this._booting)return;const t=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=t);const n=t-this._bootStart;n>=1200||n>=700&&i>0&&t-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(e,100)};this._bootTimer=window.setTimeout(e,100)}_bootSettled(){this._booting&&!this._bootSettling&&(this._bootSettling=!0,this._refitView(),this._bootSettleRaf=requestAnimationFrame(()=>{this._bootSettleRaf=0,this._finishBootSettled()}))}_finishBootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1,this._bootSettling=!1;const e=this._stageEl?.clientHeight??0;!this._config?.kiosk&&e>0&&this._warmPatch({hdrH:this._hdrH,stageH:e,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},rf)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._booting||this._resumeSettling||(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0),this._viewportInvalidAt=0,this._beginContinuityCandidate("warm-resume",!1),this._loading?this.requestUpdate():this._loadFromServer())}_refitView(){if(this._modeTransitionBusy||this._warmModeRequest)return;this._cancelCameraTransition(!1);const e=this._stageEl;if(!e||"visible"!==document.visibilityState||e.clientWidth<=0||e.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));const t=[e.clientWidth,e.clientHeight],i=this._lastValidStageSize,n=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,r=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._roomFocus?.spaceId===this._space?this._fitRoom(this._roomFocus.roomId,!1):this._view||this._applyView(this._zoom));n?this._pendingRefitSize=null:(this._pendingRefitSize=t,this._refitRaf||(this._refitRaf=requestAnimationFrame(()=>{this._refitRaf=0;const e=this._pendingRefitSize;this._pendingRefitSize=null;const t=this._stageEl;if(!e||!t||t.clientWidth<=0||t.clientHeight<=0)return;if(Math.abs(t.clientWidth-e[0])>.5||Math.abs(t.clientHeight-e[1])>.5)return void this._refitView();const i=this._lastValidStageSize;if(i&&Math.abs(i[0]-e[0])<=.5&&Math.abs(i[1]-e[1])<=.5)return;this._lastValidStageSize=e;const n=this._view;r>=Dl?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._roomFocus?.spaceId!==this._space?this._applyView(this._zoom,n?n.x+n.w/2:void 0,n?n.y+n.h/2:void 0):this._fitRoom(this._roomFocus.roomId,!1)})))}_cameraTargetAt(e,t,i,n=!1){const r=this._stageEl;if(!r||r.clientWidth<=0||r.clientHeight<=0)return null;const o=vi(this._baseVb(),this._stageAspect()),s=Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,i)),a=function(e,t,i,n,r,o,s){if(!U_(e)||!U_({zoom:t,viewBox:i})||!j_(n)||n<=0||!j_(r)||r<=0||!j_(o)||!j_(s))return null;const a=o/n,l=s/r,c=e.viewBox.x+a*e.viewBox.w,h=e.viewBox.y+l*e.viewBox.h,d=i.w/t,u=i.h/t;return{zoom:t,viewBox:{x:c-a*d,y:h-l*u,w:d,h:u}}}(n&&this._cameraTransition.target||this._cameraState(),s,o,r.clientWidth,r.clientHeight,e,t);return a?(a.viewBox=this._clampView(a.viewBox,o),{target:a,fit:o}):null}_zoomAt(e,t,i){this._clearRoomFocus(),this._cancelCameraTransition(!1);const n=this._cameraTargetAt(e,t,i);n&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=n.target.zoom,this._view={...n.target.viewBox})}_onWheel(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),n=e.deltaY<0?1.15:1/1.15,r=this._cameraTransition.target?.zoom??this._zoom,o=this._cameraTargetAt(e.clientX-i.left,e.clientY-i.top,r*n,!0);o&&this._startCameraTransition(o.target,o.fit,"wheel",160)}_stepZoom(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;const i=this._cameraTransition.target?.zoom??this._zoom,n=this._cameraTargetAt(t.clientWidth/2,t.clientHeight/2,i*(e>0?1.4:1/1.4),!0);n&&this._startCameraTransition(n.target,n.fit,"button",180)}_resetZoom(e="fit"){this._clearRoomFocus(),this._prepareCameraCommand();const t=vi(this._baseVb(),this._stageAspect());this._startCameraTransition({zoom:1,viewBox:{...t}},t,e,220)}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(hf,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){this._clearRoomFocus(!0),this._cancelCameraTransition(!1);const e=this._zoomBySpace[this._space]||1;this._zoom=e;const t=this._stageEl;if(t&&t.clientHeight){const t=this._baseVb();return this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const t=this._baseVb();this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),this.requestUpdate()})}_stagePointerDown(e){const t=e.isPrimary&&0===e.button&&"view"===this._mode?ag(e.composedPath()):null;if(this._roomPointer=t?{pointerId:e.pointerId,spaceId:this._space,roomId:t}:null,this._doubleFit.pointerDown(e,this._space,this._doubleFitEnabled),this._cancelCameraTransition(!1,!0),this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:e.clientX,y:e.clientY,id:e.pointerId},e.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._roomPointer=null,this._doubleFit.clear(),this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag||this._deviceDrag)return;if(this._markup&&e.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&e.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(e))return;this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY});const i=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:i.x,vy:i.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y);this._pinchStart={dist:t,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return!this._hasFixedFloor&&this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(e){if(this._physicalRotate?.pid!==e.pointerId)if(this._physicalDrag?.pid!==e.pointerId)if(this._dtDrag?.pid!==e.pointerId)if(this._bdDrag?.pid!==e.pointerId){if(this._decorDraft?.pid===e.pointerId){const t=this._decorDraft;let i=this._decorSnap(this._svgPoint(e),e.pointerType);if(e.shiftKey&&("rect"===t.kind||"ellipse"===t.kind)){const e=i[0]-t.a[0],n=i[1]-t.a[1],r=Math.max(Math.abs(e),Math.abs(n));i=this._snap([t.a[0]+(e<0?-r:r),t.a[1]+(n<0?-r:r)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId){if("decor"!==this._mode||"furniture"!==this._decorTool&&"image"!==this._decorTool||!this._editorRuntime||(this._notePointer(e),!this._editorRuntime._furnPointerMove(e,this._pointerModality.hoverEnabled)))if(this._pointers.has(e.pointerId)){if(this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(e),this._pinchStart&&this._pointers.size>=2){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),n=(e[0].x+e[1].x)/2-i.left,r=(e[0].y+e[1].y)/2-i.top;this._zoomAt(n,r,this._pinchStart.zoom*t),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const t=e.clientX-this._panStart.sx,i=e.clientY-this._panStart.sy;Math.abs(t)+Math.abs(i)>4&&(this._roomPointer=null,this._doubleFit.clear(),this._suppressClick=!0,clearTimeout(this._holdTimer),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover()),null===this._panLock&&Math.abs(t)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(t)>1.5*Math.abs(i)?"swipe":"pan","pan"===this._panLock&&this._clearRoomFocus());const n=this._stageEl;if("pan"===this._panLock&&n){const e=this._baseVb(),r=this._viewOr(e),o=vi(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(n.clientWidth||1)*r.w,y:this._panStart.vy-i/(n.clientHeight||1)*r.h,w:r.w,h:r.h},o)}}}else this._markupMove(e)}else this._decorMoveUpdate(e)}else this._bdMove(e);else this._dtMove(e);else this._physicalMove(e);else this._physicalRotateMove(e)}_stagePointerLeave(e){"decor"===this._mode&&this._editorRuntime?._furnPointerLeave(e),this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){const t=(i=this._roomPointer,n=e.pointerId,r=this._space,o=this._roomPointer&&"view"===this._mode?ag(e.composedPath()):null,s=this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired||this._touchSequenceMultitouch,!i||s||i.pointerId!==n||i.spaceId!==r||i.roomId!==o?null:i.roomId);var i,n,r,o,s;this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null);if(this._doubleFit.pointerUp(e,this._space,this._doubleFitEnabled,this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired)&&this._fitAll("double-tap"),this._kiosk){clearTimeout(this._kioskHoldTimer);const i=this._swipeStart;if(this._swipeStart=null,!t&&i&&i.id===e.pointerId){const t=e.clientX-i.x,n=e.clientY-i.y,r="pan"===this._panLock?null:function(e,t,i,n,r,o=60){if(i>1.001||n.length<2)return null;if(Math.abs(e)e.id),this._space);r&&this._slideTo(r,t<0?"left":"right")&&(this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid===e.pointerId)return void this._physicalUp(e);if(this._physicalRotate?.pid===e.pointerId)return void this._physicalRotateUp(e);if(this._dtDrag?.pid===e.pointerId)return void this._dtUp();if(this._bdDrag?.pid===e.pointerId)return void this._bdUp();if(this._decorDraft?.pid===e.pointerId)return void this._decorCommitDraft();if(this._decorMove?.pid===e.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);if(this._editorRuntime?._furnPointerUp(e))return;const a=!!this._pinchStart||"pan"===this._panLock;this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._suppressClick&&setTimeout(()=>this._suppressClick=!1,0)),a&&0===this._pointers.size&&!t&&this.requestUpdate(),t&&this._fitRoom(t)}_clickRoom(e){!this._suppressClick&&e.area&&vf("/config/areas/area/"+e.area)}_roomLabelKey(e,t){"view"!==this._mode||"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),e.stopPropagation(),this._fitRoom(t))}_pointerDown(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,n=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,n),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind||this._devicePositionBusy)return;if(this._deviceDrag)return void(this._deviceDrag.pointerId!==e.pointerId&&this._cancelDeviceDrag());e.preventDefault();const i=this._pos(t);this._deviceDrag={id:t.id,spaceId:t.space,displayName:t.name,pointerId:e.pointerId,source:e.currentTarget,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1,before:C_(this._layout,t.id),start:this._devicePlacementForCanvas(t,i.x,i.y)},bf(e),this._tip=null}_pointerMove(e,t){if("devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;const n=this.renderRoot.querySelector(".stage");if(!n)return;const r=this._baseVb(),o=n.getBoundingClientRect(),s=this._viewOr(r),a=(e.clientX-i.sx)/o.width*s.w,l=(e.clientY-i.sy)/o.height*s.h;Math.abs(e.clientX-i.sx)+Math.abs(e.clientY-i.sy)>3&&(i.moved=!0,clearTimeout(this._holdTimer));const c=Ea(i.ox+a),h=Ea(i.oy+l);this._previewDevicePlacement(t.id,this._devicePlacementForCanvas(t,c,h))}_pointerUp(e,t){if(clearTimeout(this._holdTimer),"devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;this._deviceDrag=null;const n=C_(this._layout,t.id);var r,o;i.moved&&null!==n&&(r=n,o=i.start,null===r||null===o?r!==o:r.x!==o.x||r.y!==o.y||r.s!==o.s)?(this._selId=t.id,this._suppressClick=!0,window.setTimeout(()=>{this._suppressClick=!1},0),this._devicePositionBusy=!0,this.requestUpdate(),this._persistDevicePlacement(t.id,n).then(()=>{const e=this._t("history.device_move",{name:i.displayName});this._devicePositionHistory.push({name:e,before:{deviceId:i.id,spaceId:i.spaceId,placement:i.before},after:{deviceId:i.id,spaceId:i.spaceId,placement:n}})}).catch(e=>{this._previewDevicePlacement(t.id,i.before),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))}).finally(()=>{this._devicePositionBusy=!1,this.requestUpdate()})):this._previewDevicePlacement(t.id,i.before)}_pointerCancel(e,t){this._deviceDrag?.id===t.id&&this._deviceDrag.pointerId===e.pointerId&&this._cancelDeviceDrag()}_showToast(e){for(const e of this.renderRoot.querySelectorAll("hp-dialog"))e.closeTransientOverlays("toast");this._toast=e,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}_syncPointerHoverTargets(){const e=this._pointerModality.hoverEnabled;for(const t of this.renderRoot.querySelectorAll(mf))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(mf)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(mf))e.toggleAttribute("data-pointer-hover",i)}_clearTransientHover(e=!1){e&&this._pointerModality.suspend(),this._tip&&(this._tip=null),this._hoverRoom&&(this._hoverRoom=null)}_notePointer(e){const t=this._pointerModality.modality,i=this._pointerModality.note(e);"touch"!==i&&"pen"!==i||t===i&&!this._tip&&!this._hoverRoom||this._clearTransientHover()}_guardTouchGesture(e){if(this._editorSecondary?.handleOutsideDismiss(e))return;if("click"===e.type){if(!this._suppressClick&&!this._touchSequenceMultitouch&&Date.now()>this._touchClickBlockUntil)return;return e.preventDefault(),void e.stopImmediatePropagation()}const t=e;if(this._notePointer(t),"pointercancel"===e.type||"lostpointercapture"===e.type?this._doubleFit.clear():"pointerdown"===e.type&&this._doubleFit.clearOutside(t),"touch"===t.pointerType)if("pointerdown"!==e.type){if("pointermove"===e.type){const e=this._touchContacts.get(t.pointerId);if(!e)return;const i={...e,x:t.clientX,y:t.clientY};if(this._touchContacts.set(t.pointerId,i),this._touchSequenceMultitouch&&"view"===this._mode&&!this._vacFit&&this._pinchStart&&[...this._touchContacts.values()].every(e=>e.inStage)){this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=[...this._touchContacts.values()];if(e.length>=2&&this._stageEl){const[t,i]=e,n=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),r=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-r.left,(t.y+i.y)/2-r.top,this._pinchStart.zoom*n),this._saveZoom()}}return}if("pointerup"===e.type||"pointercancel"===e.type||"lostpointercapture"===e.type){if(this._clearTransientHover(),this._touchContacts.delete(t.pointerId),this._touchSequenceMultitouch){if(this._touchClickBlockUntil=Date.now()+500,this._pointers.delete(t.pointerId),!this._vacFit&&this._pointers.size>=2){const[e,t]=[...this._pointers.values()];this._pinchStart={dist:Math.hypot(e.x-t.x,e.y-t.y),zoom:this._zoom}}else this._pinchStart=null;0===this._pointers.size&&(this._panStart=null,this._panLock=null)}0===this._touchContacts.size&&(this._touchSequenceMultitouch=!1)}}else if(this._touchContacts.set(t.pointerId,{x:t.clientX,y:t.clientY,inStage:!!t.target?.closest?.(".stage")}),this._touchContacts.size>=2){this._clearRoomFocus(!0),this._clearTransientHover(),this._touchSequenceMultitouch=!0,this._touchClickBlockUntil=Number.POSITIVE_INFINITY,clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),this._swipeStart=null;const e=[...this._touchContacts.values()];if("view"===this._mode&&!this._vacFit&&e.every(e=>e.inStage)){this._pointers=new Map([...this._touchContacts].map(([e,t])=>[e,{x:t.x,y:t.y}]));const[t,i]=e;this._pinchStart={dist:Math.hypot(t.x-i.x,t.y-i.y),zoom:this._zoom},this._panStart=null,this._panLock=null}else this._vacFit&&(this._pointers.clear(),this._pinchStart=null,this._panStart=null,this._panLock=null)}}_showTip(e,t,i,n,r,o,s=!1){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||this._deviceDrag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:n,temp:r,hum:o,room:s}))}_roomTipEnabledForPointer(e){return"view"===this._mode&&(this._notePointer(e),!!Ki(this._settings)||(this._tip?.room&&(this._tip=null),!1))}get _gridPitch(){return Pa}get _cellCm(){const e=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(e)&&e>0?e:5}_fmtLen(e,t){const i=function(e,t,i,n){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*n}(e,t,this._gridPitch,this._cellCm);return Et(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const e=this._resize?.preview;return e&&e.space===this._space?e.sp:this._serverCfg?.spaces.find(e=>e.id===this._space)}get _renderCfg(){const e=this._resize?.preview;return e&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(t=>t.id===e.space?e.sp:t)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,_f}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return Bt(e?.rooms||[]).map(e=>[e[0]*_f,e[1]*t,e[2]*_f,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(df)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(df,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,this._clearRoomFocus(!0),this._cancelDangerConfirm(),"view"!==this._mode&&this._setMode("view",!1),this._pendingNavMode=null,this._geometryHistory.clear(),this._activeDraftId=null,this._resumeDraftBySpace={},this._draftSegmentCms=[],this._closingWallCm=null,this._drawWallField=null,this._showHidden=!1,this._tapConfirm=null,this._vacCalConfirm=null,this._decorEraseConfirm=null,this._openingInfo=null,this._closeInfoCard(),this._rulesDialog=null,this._alignDialog=null,this._preflightClipboardFallback=null,this._backupImportDialog=null,this._backupExportDialog=null,this._supportDialog?.preview?.token&&this._editorRuntime?._discardSupportPreview(this._supportDialog.preview.token),this._supportDialog=null,this._settingsDialog=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){return e!==this._mode&&(this._clearRoomFocus(!0),this._cancelDangerConfirm()),this._warmModeRequest=0,this._editorRuntime?this._editorRuntime._setMode(e,t):"view"===e?void this._editorModeRequest++:void this._requestMode(e,t)}_primeDrawWallField(){return this._editorRuntimeOrThrow()._primeDrawWallField()}get _drawWallFieldValue(){return null===this._drawWallField?uo(15,this._imperial):this._drawWallField}get _drawWallCm(){const e=(e=>{const t=String(e??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t))return null;const i=Number(t);return Number.isFinite(i)?i:null})(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Vs:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Vs:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){return this._editorRuntimeOrThrow()._showPhysicalRange(e,t)}_draftSegmentCount(e=this._curSpaceCfg){return this._editorRuntimeOrThrow()._draftSegmentCount(e)}_mergeSpacePartitions(e,t){return this._editorRuntimeOrThrow()._mergeSpacePartitions(e,t)}_finishWallChain(){return this._editorRuntimeOrThrow()._finishWallChain()}_activateMarkupTool(e){return this._editorRuntimeOrThrow()._activateMarkupTool(e)}_limitReached(e){return this._editorRuntimeOrThrow()._limitReached(e)}_svgPoint(e){return this._editorRuntimeOrThrow()._svgPoint(e)}_snap(e){return this._editorRuntimeOrThrow()._snap(e)}_snapDrawPoint(e,t=!1){return this._editorRuntimeOrThrow()._snapDrawPoint(e,t)}_planSnapOpeningCuts(e,t){return this._editorRuntimeOrThrow()._planSnapOpeningCuts(e,t)}_planSnapGeometrySnapshot(){return this._editorRuntimeOrThrow()._planSnapGeometrySnapshot()}_hiddenWallDiagnosticSnapshot(){return this._editorRuntimeOrThrow()._hiddenWallDiagnosticSnapshot()}_planStructuralGeometrySnapshot(){return this._editorRuntimeOrThrow()._planStructuralGeometrySnapshot()}_planSnapContextKey(e){return this._editorRuntimeOrThrow()._planSnapContextKey(e)}_resolvePlanDrawPoint(e,t){return this._editorRuntimeOrThrow()._resolvePlanDrawPoint(e,t)}get _activePlanSnapCandidate(){if(!this._markup||"draw"!==this._tool)return null;const e=this._planSnapHover;if(!e)return null;const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.candidate:null}get _activePlanSnapConflicts(){if(!this._markup||"draw"!==this._tool)return[];const e=this._planSnapHover;if(!e)return[];const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.conflicts:[]}_clearPlanSnapHover(e=!0){return this._editorRuntime?this._editorRuntimeOrThrow()._clearPlanSnapHover(e):(this._planSnapHover=null,void(e&&(this._cursorPt=null)))}_samePt(e,t){return Xt(e,t)}_dropLegacySegments(){return this._editorRuntimeOrThrow()._dropLegacySegments()}_rollbackRejectedPhysicalWrites(e){return this._editorRuntimeOrThrow()._rollbackRejectedPhysicalWrites(e)}async _reloadRejectedPhysicalWrite(){return this._editorRuntimeOrThrow()._reloadRejectedPhysicalWrite()}get _cfgWriting(){return this._writesPending>0}async _sendConfigCandidate(e){const t=to(e),i=await this.hass.callWS({type:"houseplan/config/set",config:t,expected_rev:this._cfgRev});this._cfgRev=i?.rev??this._cfgRev+1}_writeConfig(){if(this._editorRuntime)return this._editorRuntime._writeConfig();this._writesPending++,this._writeChain=x_(this._writeChain,async()=>{if(!this._serverCfg)return;const e=to(this._serverCfg),t=Il(e);t!==Il(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t,await this._sendConfigCandidate(e)});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){return this._editorRuntimeOrThrow()._geometrySnapshotFromConfig(e,t)}_geometrySnapshot(e=this._space){return this._editorRuntimeOrThrow()._geometrySnapshot(e)}_recordGeometry(e,t){return this._editorRuntimeOrThrow()._recordGeometry(e,t)}_restoreGeometryStateInConfig(e,t,i=!1){return this._editorRuntimeOrThrow()._restoreGeometryStateInConfig(e,t,i)}_restoreGeometryStateLocal(e){return this._editorRuntimeOrThrow()._restoreGeometryStateLocal(e)}_wallModelBlockerLabel(e){return this._editorRuntimeOrThrow()._wallModelBlockerLabel(e)}_hasLegacyZeroWallFields(e=this._serverCfg){return this._editorRuntimeOrThrow()._hasLegacyZeroWallFields(e)}_showWallModelMigrationBlocked(e){return this._editorRuntimeOrThrow()._showWallModelMigrationBlocked(e)}_limitSegmentsOf(e){return this._editorRuntimeOrThrow()._limitSegmentsOf(e)}_junctionLimitViolations(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitViolations(e,t,i)}_junctionLimitLabel(e){return this._editorRuntimeOrThrow()._junctionLimitLabel(e)}_junctionLimitsIntroduced(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitsIntroduced(e,t,i)}_commitPhysicalGeometry(e,t,i=[]){return this._editorRuntimeOrThrow()._commitPhysicalGeometry(e,t,i)}_clearGeometryGesture(){return this._editorRuntime?this._editorRuntimeOrThrow()._clearGeometryGesture():(this._path=[],this._planSnapHover=null,this._cursorPt=null,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingPreset=null,this._openingRebindId=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._roomDeleteDialog=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._resize?.reset(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,void(this._bdDrag=null))}_stagePointerCancel(e){if(this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null),this._doubleFit.clear(),this._editorRuntime)return this._editorRuntime._stagePointerCancel(e);const t=!!this._pinchStart||!!this._panStart;this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._swipeStart=null),t&&0===this._pointers.size&&this.requestUpdate()}_applyGeometryState(e,t=!1){return this._editorRuntimeOrThrow()._applyGeometryState(e,t)}_roomAt(e){return this._editorRuntimeOrThrow()._roomAt(e)}_overlapRoom(e){return this._editorRuntimeOrThrow()._overlapRoom(e)}_pointInRoom(e,t){return t.poly?Zt(e,t.poly):null!=t.x&&e[0]>=t.x&&e[0]<=t.x+t.w&&e[1]>=t.y&&e[1]<=t.y+t.h}_contourSelfIntersects(e){return this._editorRuntimeOrThrow()._contourSelfIntersects(e)}_canAppendRoomDraftPoint(){return this._editorRuntimeOrThrow()._canAppendRoomDraftPoint()}_markupClick(e){if(this._editorRuntime)return this._editorRuntime._markupClick(e)}_draftEndAt(e,t){return this._editorRuntimeOrThrow()._draftEndAt(e,t)}_mergeDraftEndpoint(e){return this._editorRuntimeOrThrow()._mergeDraftEndpoint(e)}_adoptDraftCms(e,t,i){return this._editorRuntimeOrThrow()._adoptDraftCms(e,t,i)}_draftSegmentsForPath(e,t,i){return this._editorRuntimeOrThrow()._draftSegmentsForPath(e,t,i)}_persistActiveDraftSegment(){return this._editorRuntimeOrThrow()._persistActiveDraftSegment()}_activeWallSourceKey(e){return this._editorRuntimeOrThrow()._activeWallSourceKey(e)}_wallGraphSources(e){return this._editorRuntimeOrThrow()._wallGraphSources(e)}_wallFaceGraph(e,t){return this._editorRuntimeOrThrow()._wallFaceGraph(e,t)}_offerWallFaces(e,t=this._path.length-2,i){return this._editorRuntimeOrThrow()._offerWallFaces(e,t,i)}_beginWallFaceBatch(e){return this._editorRuntimeOrThrow()._beginWallFaceBatch(e)}_offerExistingWallFace(e){return this._editorRuntimeOrThrow()._offerExistingWallFace(e)}_columnClick(e){return this._editorRuntimeOrThrow()._columnClick(e)}_openPhysicalDialog(e,t,i){return this._editorRuntimeOrThrow()._openPhysicalDialog(e,t,i)}_physicalDown(e,t,i){return this._editorRuntimeOrThrow()._physicalDown(e,t,i)}_clampPhysicalDelta(e,t,i){return this._editorRuntimeOrThrow()._clampPhysicalDelta(e,t,i)}_physicalMove(e){return this._editorRuntimeOrThrow()._physicalMove(e)}_physicalUp(e){return this._editorRuntimeOrThrow()._physicalUp(e)}_registerPhysicalTap(e,t,i){return this._editorRuntimeOrThrow()._registerPhysicalTap(e,t,i)}_cancelPhysicalGesture(){return this._editorRuntimeOrThrow()._cancelPhysicalGesture()}_physicalRotateDown(e,t){return this._editorRuntimeOrThrow()._physicalRotateDown(e,t)}_physicalRotateMove(e){return this._editorRuntimeOrThrow()._physicalRotateMove(e)}_physicalRotateUp(e){return this._editorRuntimeOrThrow()._physicalRotateUp(e)}_rszRooms(){return this._editorRuntimeOrThrow()._rszRooms()}_rszOpenings(){return this._editorRuntimeOrThrow()._rszOpenings()}_rszObstacles(){return this._editorRuntimeOrThrow()._rszObstacles()}_rszOptsFor(e,t){return this._editorRuntimeOrThrow()._rszOptsFor(e,t)}_rszResolution(e,t,i){return this._editorRuntimeOrThrow()._rszResolution(e,t,i)}_rszSnapshot(){return this._editorRuntimeOrThrow()._rszSnapshot()}_rszResetController(){return this._editorRuntimeOrThrow()._rszResetController()}_rszProjectPreview(e,t,i,n,r){return this._editorRuntimeOrThrow()._rszProjectPreview(e,t,i,n,r)}_rszAcceptPreview(e,t){return this._editorRuntimeOrThrow()._rszAcceptPreview(e,t)}_rszSpaceCandidateGeometry(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateGeometry(e,t)}_rszSpaceCandidateRenderable(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateRenderable(e,t)}_rszCandidateRenderable(e){return this._editorRuntimeOrThrow()._rszCandidateRenderable(e)}_rszEdgeDown(e,t,i){return this._editorRuntimeOrThrow()._rszEdgeDown(e,t,i)}_rszReasonText(e){return this._editorRuntimeOrThrow()._rszReasonText(e)}_rszDisabledActivate(e,t){return this._editorRuntimeOrThrow()._rszDisabledActivate(e,t)}_rszDisabledKey(e,t){return this._editorRuntimeOrThrow()._rszDisabledKey(e,t)}_rszMove(e){return this._editorRuntimeOrThrow()._rszMove(e)}_rszUp(e){return this._editorRuntimeOrThrow()._rszUp(e)}_rszCancelDrag(e){return this._editorRuntimeOrThrow()._rszCancelDrag(e)}_rszPointerCancel(e){return this._editorRuntimeOrThrow()._rszPointerCancel(e)}_rszEdgeLabels(e,t,i=this._resize?.rooms){return this._editorRuntimeOrThrow()._rszEdgeLabels(e,t,i)}_rszInnerSpanCms(e,t,i){return this._editorRuntimeOrThrow()._rszInnerSpanCms(e,t,i)}_renderResizeMeasurements(){return this._editorRuntimeOrThrow()._renderResizeMeasurements()}_renderResizeLayer(e){return this._editorRuntimeOrThrow()._renderResizeLayer(e)}get _openingsR(){const e=this._curSpaceCfg,t=this._spaceH,i=this._spaceModel();return i?(e?.openings||[]).flatMap(e=>{const n={...e,rx:e.x*_f,ry:e.y*t,rlen:e.length*_f};if(!e.host||"wall"===e.host.kind)return[n];const r=Zd(e,i.partitions,_f,this._cellCm,this._gridPitch);if(!r.resolved)return"plan"===this._mode?[{...n,orphanReason:r.reason||"invalid-host"}]:[];let[o,s]=r.resolved.center;const a=this._physicalDrag;return a?.moved&&"partition"===a.kind&&a.id===e.host.id&&(o+=a.delta[0],s+=a.delta[1]),[{...e,rx:o,ry:s,rlen:r.resolved.length,angle:r.resolved.angle,partitionHost:r.resolved}]}):[]}_partitionOpeningCuts(e=this._spaceModel(),t=()=>!0){if(!e)return[];return Lp(Hp(this._curSpaceCfg?.id===e.id?this._curSpaceCfg:null,e,this._cellCm,this._gridPitch,_f),t)}_roomWallOpeningInputs(e=this._openingsR,t=this._spaceModel()){if(!t)return[];const i=this._openCuts();return Bp(e,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f)}_openingFace(e,t,i){return e.partitionHost?tu(e.partitionHost,i):qs(t,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:i})}_cmToUnits(e){return e/this._cellCm*this._gridPitch}get _decorList(){const e=this._curSpaceCfg;return Array.isArray(e?.decor)?e.decor:[]}get _decorH(){return _f}_decorResolvedStyle(e){return function(e,t,i,n=rr){const r="rect"===e?.kind||"ellipse"===e?.kind,o=e,s=Dt(o?.color,n.color);return{color:s,opacity:or(o?.opacity,n.opacity),widthCm:dr(e,t,i,n.widthCm),fill:!!r&&!0===o?.fill,fillColor:Dt(o?.fill_color,o?.fill?s:n.fillColor),fillOpacity:r&&o?.fill?or(o?.fill_opacity,.25):n.fillOpacity}}(e,this._cellCm,this._gridPitch,rr)}_decorWidthUnits(e){return((e,t,i,n=rr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return cr(r,t,i);const o=Number(e?.width);return Number.isFinite(o)&&o>0?o:cr(n,t,i)})(e,this._cellCm,this._gridPitch,rr.widthCm)}_decorTextSizeCm(e){if("text"===e?.kind){const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?t:hr(20*Ei(e),this._cellCm,this._gridPitch)}return hr(20,this._cellCm,this._gridPitch)}_decorTextUnits(e){if("text"!==e.kind)return 20;const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?cr(t,this._cellCm,this._gridPitch):20*Ei(e)}_decorSmallField(e){return Math.round(100*(this._imperial?e/2.54:e))/100}_decorSmallCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(100,t)):.1}_decorTextCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(2e3,t)):.1}_decorLargeField(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_decorLargeCm(e){const t=this._imperial?30.48*e:100*e;return Number.isFinite(t)?Math.max(.1,Math.min(Da*this._cellCm,t)):.1}_angleField(e){const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(3))):"0"}_decorBoxOf(e){return"rect"!==e.kind&&"ellipse"!==e.kind&&"furniture"!==e.kind&&"image"!==e.kind?null:{x:e.x*_f,y:e.y*this._decorH,w:e.w*_f,h:e.h*this._decorH,angle:sr(e.angle)||void 0}}_decorSnapGeometry(e){return this._editorRuntimeOrThrow()._decorSnapGeometry(e)}_decorSnap(e,t="mouse",i){return this._editorRuntimeOrThrow()._decorSnap(e,t,i)}_replaceDecor(e,t){return this._editorRuntimeOrThrow()._replaceDecor(e,t)}_cancelDecorGesture(){return this._editorRuntimeOrThrow()._cancelDecorGesture()}_decorPointerDown(e){return this._editorRuntimeOrThrow()._decorPointerDown(e)}_decorCommitDraft(){return this._editorRuntimeOrThrow()._decorCommitDraft()}_decorShapeDown(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDown(e,t)}_decorMoveUpdate(e){return this._editorRuntimeOrThrow()._decorMoveUpdate(e)}_decorNudge(e,t){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag)return!1;const i=this._decorList.find(e=>e.id===this._decorSel),n=this._curSpaceCfg;if(!i||!n)return!1;const r=function(e,t,i,n,r,o){if(!([t,i,n,r,o].every(Number.isFinite)&&r>0))return null;let s;if(s="line"===e.kind?[[e.x1,e.y1],[e.x2,e.y2]]:"text"===e.kind?[[e.x,e.y]]:mr(e),!s.length||s.some(e=>!e.slice(0,2).every(Number.isFinite)))return null;const a=s.map(e=>e[0]),l=s.map(e=>e[1]),c=Math.min(...a),h=Math.max(...a),d=Math.min(...l),u=Math.max(...l),p=t/n,m=i/r,_=p>0?Math.min(p,Math.max(0,o-h)):Math.max(p,Math.min(0,-o-c)),g=m>0?Math.min(m,Math.max(0,o-u)):Math.max(m,Math.min(0,-o-d));return _||g?"line"===e.kind?{...e,x1:e.x1+_,y1:e.y1+g,x2:e.x2+_,y2:e.y2+g}:{...e,x:e.x+_,y:e.y+g}:null}(i,e,t,_f,this._decorH,Da);if(!r)return!1;const o=this._geometrySnapshot();return n.decor=this._decorList.map(e=>e.id===i.id?r:e),this._recordGeometry(this._t("history.decor_move"),o),this._saveConfig(),this.requestUpdate(),!0}_decorShapeDbl(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDbl(e,t)}_openDecorProperties(e){return this._editorRuntimeOrThrow()._openDecorProperties(e)}_decorOpenText(e){return this._editorRuntimeOrThrow()._decorOpenText(e)}_decorRememberTextSelection(e){return this._editorRuntimeOrThrow()._decorRememberTextSelection(e)}_decorInsertLiveVariable(e){return this._editorRuntimeOrThrow()._decorInsertLiveVariable(e)}_decorSaveText(){return this._editorRuntimeOrThrow()._decorSaveText()}_decorSaveShape(){return this._editorRuntimeOrThrow()._decorSaveShape()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(e=>e.id===this._decorSel)||null}_dtPivot(e){return this._editorRuntimeOrThrow()._dtPivot(e)}_dtApply(e,t){return this._editorRuntimeOrThrow()._dtApply(e,t)}_dtStart(e,t,i,n){return this._editorRuntimeOrThrow()._dtStart(e,t,i,n)}_dtMove(e){return this._editorRuntimeOrThrow()._dtMove(e)}_dtUp(){return this._editorRuntimeOrThrow()._dtUp()}_dtMeasure(){return this._editorRuntimeOrThrow()._dtMeasure()}_deleteDecor(e){return this._editorRuntimeOrThrow()._deleteDecor(e)}_decorDeleteSel(){return this._editorRuntimeOrThrow()._decorDeleteSel()}_confirmDecorErase(){return this._editorRuntimeOrThrow()._confirmDecorErase()}get _bdBase(){const e=this._curSpaceCfg;return e?.plan_url?{...$a(e.plan_aspect,_f)}:null}get _bdRect(){const e=this._curSpaceCfg;return e?.plan_url?Ca(e,_f):null}get _bdParams(){const e=this._curSpaceCfg,t=Number(e?.plan_x),i=Number(e?.plan_y),n=Number(e?.plan_scale),r=Number(e?.plan_scale_x),o=Number(e?.plan_scale_y),s=Number.isFinite(n)&&n>0?n:1;return{dx:Number.isFinite(t)?t:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(r)&&r>0?r:s,sy:Number.isFinite(o)&&o>0?o:s,angle:sr(e?.plan_angle)}}_openBackdropDialog(e){if(this._editorRuntime)return this._editorRuntime._openBackdropDialog(e)}_saveBackdropDialog(){return this._editorRuntimeOrThrow()._saveBackdropDialog()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(e,t,i,n,r){return this._editorRuntimeOrThrow()._bdApply(e,t,i,n,r)}_bdStart(e,t,i=!1){return this._editorRuntimeOrThrow()._bdStart(e,t,i)}_bdMove(e){return this._editorRuntimeOrThrow()._bdMove(e)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const e=this._bdParams;return 0!==e.dx||0!==e.dy||1!==e.sx||1!==e.sy||0!==e.angle}_bdReset(){return this._editorRuntimeOrThrow()._bdReset()}_bdUp(){return this._editorRuntimeOrThrow()._bdUp()}get _bdLive(){if(!this._bdDrag)return null;const e=this._bdRect;return e?{x:e.x+e.w/2,y:e.y+e.h/2,text:`${this._fmtLen([0,0],[e.w,0])} × ${this._fmtLen([0,0],[0,e.h])}`}:null}_renderBackdropFrame(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderBackdropFrame(e):V}_renderTextFrame(e){const t=this._dtSel,i=this._dtBox;if(!t||!i||i.id!==t.id)return V;const n=.018*Math.max(e.w,e.h),r=n/4;if("line"===t.kind){const e=[t.x1*_f,t.y1*this._decorH],i=[t.x2*_f,t.y2*this._decorH];return j` + `}_stageAspect(){const e=this._stageEl,t=this._baseVb();return e&&e.clientHeight?e.clientWidth/e.clientHeight:t[2]/t[3]}_viewOr(e){return this._view&&this._view.w?this._view:vi(e,this._stageAspect())}_roomLabelReferenceViewWidth(e){if(!this._markup)return e.w;const t=this._stageEl,i=this.renderRoot.querySelector(".editorchrome");if(!t||t.clientWidth<=0||t.clientHeight<=0)return e.w;const n=t.clientHeight+(i?.getBoundingClientRect().height||0);return n<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,n).w}_screenToVb(e,t){const i=this._stageEl,n=this._viewOr(this._baseVb()),r=i?.clientWidth||1,o=i?.clientHeight||1;return[n.x+e/r*n.w,n.y+t/o*n.h]}_clampView(e,t){const i=(e,t,i,n)=>{const r=1*Math.max(t,n),o=i-r,s=i+n-t+r;return Math.max(Math.min(o,s),Math.min(Math.max(o,s),e))};return{w:e.w,h:e.h,x:i(e.x,e.w,t.x,t.w),y:i(e.y,e.h,t.y,t.h)}}_applyView(e,t,i){this._cancelCameraTransition(!1);const n=this._baseVb(),r=vi(n,this._stageAspect()),o=Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,e)),s=r.w/o,a=r.h/o,l=this._viewOr(n),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-s/2,y:h-a/2,w:s,h:a},r);return!(this._view&&Math.abs(this._zoom-o)<1e-9&&Math.abs(this._view.x-d.x)<1e-6&&Math.abs(this._view.y-d.y)<1e-6&&Math.abs(this._view.w-d.w)<1e-6&&Math.abs(this._view.h-d.h)<1e-6)&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=o,this._view=d,this._liveVp(),!0)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const e=()=>{if(!this._booting)return;const t=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=t);const n=t-this._bootStart;n>=1200||n>=700&&i>0&&t-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(e,100)};this._bootTimer=window.setTimeout(e,100)}_bootSettled(){this._booting&&!this._bootSettling&&(this._bootSettling=!0,this._refitView(),this._bootSettleRaf=requestAnimationFrame(()=>{this._bootSettleRaf=0,this._finishBootSettled()}))}_finishBootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1,this._bootSettling=!1;const e=this._stageEl?.clientHeight??0;!this._config?.kiosk&&e>0&&this._warmPatch({hdrH:this._hdrH,stageH:e,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},lf)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._booting||this._resumeSettling||(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0),this._viewportInvalidAt=0,this._beginContinuityCandidate("warm-resume",!1),this._loading?this.requestUpdate():this._loadFromServer())}_refitView(){if(this._modeTransitionBusy||this._warmModeRequest)return;this._cancelCameraTransition(!1);const e=this._stageEl;if(!e||"visible"!==document.visibilityState||e.clientWidth<=0||e.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));const t=[e.clientWidth,e.clientHeight],i=this._lastValidStageSize,n=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,r=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._roomFocus?.spaceId===this._space?this._fitRoom(this._roomFocus.roomId,!1):this._view||this._applyView(this._zoom));n?this._pendingRefitSize=null:(this._pendingRefitSize=t,this._refitRaf||(this._refitRaf=requestAnimationFrame(()=>{this._refitRaf=0;const e=this._pendingRefitSize;this._pendingRefitSize=null;const t=this._stageEl;if(!e||!t||t.clientWidth<=0||t.clientHeight<=0)return;if(Math.abs(t.clientWidth-e[0])>.5||Math.abs(t.clientHeight-e[1])>.5)return void this._refitView();const i=this._lastValidStageSize;if(i&&Math.abs(i[0]-e[0])<=.5&&Math.abs(i[1]-e[1])<=.5)return;this._lastValidStageSize=e;const n=this._view;r>=zl?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._roomFocus?.spaceId!==this._space?this._applyView(this._zoom,n?n.x+n.w/2:void 0,n?n.y+n.h/2:void 0):this._fitRoom(this._roomFocus.roomId,!1)})))}_cameraTargetAt(e,t,i,n=!1){const r=this._stageEl;if(!r||r.clientWidth<=0||r.clientHeight<=0)return null;const o=vi(this._baseVb(),this._stageAspect()),s=Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,i)),a=function(e,t,i,n,r,o,s){if(!Y_(e)||!Y_({zoom:t,viewBox:i})||!K_(n)||n<=0||!K_(r)||r<=0||!K_(o)||!K_(s))return null;const a=o/n,l=s/r,c=e.viewBox.x+a*e.viewBox.w,h=e.viewBox.y+l*e.viewBox.h,d=i.w/t,u=i.h/t;return{zoom:t,viewBox:{x:c-a*d,y:h-l*u,w:d,h:u}}}(n&&this._cameraTransition.target||this._cameraState(),s,o,r.clientWidth,r.clientHeight,e,t);return a?(a.viewBox=this._clampView(a.viewBox,o),{target:a,fit:o}):null}_zoomAt(e,t,i){this._clearRoomFocus(),this._cancelCameraTransition(!1);const n=this._cameraTargetAt(e,t,i);n&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=n.target.zoom,this._view={...n.target.viewBox},this._liveVp())}_onWheel(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),n=e.deltaY<0?1.15:1/1.15,r=this._cameraTransition.target?.zoom??this._zoom,o=this._cameraTargetAt(e.clientX-i.left,e.clientY-i.top,r*n,!0);o&&this._startCameraTransition(o.target,o.fit,"wheel",160)}_stepZoom(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;const i=this._cameraTransition.target?.zoom??this._zoom,n=this._cameraTargetAt(t.clientWidth/2,t.clientHeight/2,i*(e>0?1.4:1/1.4),!0);n&&this._startCameraTransition(n.target,n.fit,"button",180)}_resetZoom(e="fit"){this._clearRoomFocus(),this._prepareCameraCommand();const t=vi(this._baseVb(),this._stageAspect());this._startCameraTransition({zoom:1,viewBox:{...t}},t,e,220)}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(mf,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){this._clearRoomFocus(!0),this._cancelCameraTransition(!1);const e=this._zoomBySpace[this._space]||1;this._zoom=e;const t=this._stageEl;if(t&&t.clientHeight){const t=this._baseVb();return this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const t=this._baseVb();this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),this.requestUpdate()})}_stagePointerDown(e){const t=e.isPrimary&&0===e.button&&"view"===this._mode?dg(e.composedPath()):null;if(this._roomPointer=t?{pointerId:e.pointerId,spaceId:this._space,roomId:t}:null,this._doubleFit.pointerDown(e,this._space,this._doubleFitEnabled),this._cancelCameraTransition(!1,!0),this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:e.clientX,y:e.clientY,id:e.pointerId},e.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._roomPointer=null,this._doubleFit.clear(),this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag||this._deviceDrag)return;if(this._markup&&e.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&e.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(e))return;this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY});const i=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:i.x,vy:i.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y);this._pinchStart={dist:t,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return!this._hasFixedFloor&&this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(e){if(this._physicalRotate?.pid!==e.pointerId)if(this._physicalDrag?.pid!==e.pointerId)if(this._dtDrag?.pid!==e.pointerId)if(this._bdDrag?.pid!==e.pointerId){if(this._decorDraft?.pid===e.pointerId){const t=this._decorDraft;let i=this._decorSnap(this._svgPoint(e),e.pointerType);if(e.shiftKey&&("rect"===t.kind||"ellipse"===t.kind)){const e=i[0]-t.a[0],n=i[1]-t.a[1],r=Math.max(Math.abs(e),Math.abs(n));i=this._snap([t.a[0]+(e<0?-r:r),t.a[1]+(n<0?-r:r)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId){if("decor"!==this._mode||"furniture"!==this._decorTool&&"image"!==this._decorTool||!this._editorRuntime||(this._notePointer(e),!this._editorRuntime._furnPointerMove(e,this._pointerModality.hoverEnabled)))if(this._pointers.has(e.pointerId)){if(this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(e),this._pinchStart&&this._pointers.size>=2){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),n=(e[0].x+e[1].x)/2-i.left,r=(e[0].y+e[1].y)/2-i.top;this._zoomAt(n,r,this._pinchStart.zoom*t),this._viewportGestureDirty=!0,this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const t=e.clientX-this._panStart.sx,i=e.clientY-this._panStart.sy;Math.abs(t)+Math.abs(i)>4&&(this._roomPointer=null,this._doubleFit.clear(),this._suppressClick=!0,clearTimeout(this._holdTimer),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover()),null===this._panLock&&Math.abs(t)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(t)>1.5*Math.abs(i)?"swipe":"pan","pan"===this._panLock&&this._clearRoomFocus());const n=this._stageEl;if("pan"===this._panLock&&n){const e=this._baseVb(),r=this._viewOr(e),o=vi(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(n.clientWidth||1)*r.w,y:this._panStart.vy-i/(n.clientHeight||1)*r.h,w:r.w,h:r.h},o),this._viewportGestureDirty=!0,this._liveVp()}}}else this._markupMove(e)}else this._decorMoveUpdate(e)}else this._bdMove(e);else this._dtMove(e);else this._physicalMove(e);else this._physicalRotateMove(e)}_stagePointerLeave(e){this._editorRuntime?._cancelPointerMove("markup-hover"),"decor"===this._mode&&this._editorRuntime?._furnPointerLeave(e),this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){this._flushHa(),this._editorRuntime?._cancelPointerMove("markup-hover");const t=(i=this._roomPointer,n=e.pointerId,r=this._space,o=this._roomPointer&&"view"===this._mode?dg(e.composedPath()):null,s=this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired||this._touchSequenceMultitouch,!i||s||i.pointerId!==n||i.spaceId!==r||i.roomId!==o?null:i.roomId);var i,n,r,o,s;this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null);if(this._doubleFit.pointerUp(e,this._space,this._doubleFitEnabled,this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired)&&this._fitAll("double-tap"),this._kiosk){clearTimeout(this._kioskHoldTimer);const i=this._swipeStart;if(this._swipeStart=null,!t&&i&&i.id===e.pointerId){const t=e.clientX-i.x,n=e.clientY-i.y,r="pan"===this._panLock?null:function(e,t,i,n,r,o=60){if(i>1.001||n.length<2)return null;if(Math.abs(e)e.id),this._space);r&&this._slideTo(r,t<0?"left":"right")&&(this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}this._physicalDrag?.pid!==e.pointerId?this._physicalRotate?.pid!==e.pointerId?this._dtDrag?.pid!==e.pointerId?this._bdDrag?.pid!==e.pointerId?this._decorDraft?.pid!==e.pointerId?this._decorMove?.pid!==e.pointerId?this._editorRuntime?._furnPointerUp(e)||(this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._suppressClick&&setTimeout(()=>this._suppressClick=!1,0)),this._viewportGestureDirty&&0===this._pointers.size&&!t&&(this._viewportGestureDirty=!1,this.requestUpdate()),t&&this._fitRoom(t)):this._editorRuntimeOrThrow()._dmUp():this._decorCommitDraft():this._bdUp():this._dtUp():this._physicalRotateUp(e):this._physicalUp(e)}_clickRoom(e){!this._suppressClick&&e.area&&kf("/config/areas/area/"+e.area)}_roomLabelKey(e,t){"view"!==this._mode||"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),e.stopPropagation(),this._fitRoom(t))}_pointerDown(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,n=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,n),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind||this._devicePositionBusy)return;if(this._deviceDrag)return void(this._deviceDrag.pointerId!==e.pointerId&&this._cancelDeviceDrag());e.preventDefault();const i=this._pos(t);this._deviceDrag={id:t.id,spaceId:t.space,displayName:t.name,pointerId:e.pointerId,source:e.currentTarget,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1,before:T_(this._layout,t.id),start:this._devicePlacementForCanvas(t,i.x,i.y)},Sf(e),this._tip=null,this._syncLiveHover()}_pointerMove(e,t){if("devices"!==this._mode)return;const i=this._deviceDrag;i&&i.id===t.id&&i.pointerId===e.pointerId&&this._editorRuntimeOrThrow()._queuePointerMove("device",()=>this._pointerMoveNow(e,t))}_pointerMoveNow(e,t){if("devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;const n=this.renderRoot.querySelector(".stage");if(!n)return;const r=this._baseVb(),o=n.getBoundingClientRect(),s=this._viewOr(r),a=(e.clientX-i.sx)/o.width*s.w,l=(e.clientY-i.sy)/o.height*s.h;Math.abs(e.clientX-i.sx)+Math.abs(e.clientY-i.sy)>3&&(i.moved=!0,clearTimeout(this._holdTimer));const c=Na(i.ox+a),h=Na(i.oy+l);this._previewDevicePlacement(t.id,this._devicePlacementForCanvas(t,c,h))}_pointerUp(e,t){if(clearTimeout(this._holdTimer),"devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;this._editorRuntimeOrThrow()._flushPointerMove("device"),this._deviceDrag=null;const n=T_(this._layout,t.id);var r,o;i.moved&&null!==n&&(r=n,o=i.start,null===r||null===o?r!==o:r.x!==o.x||r.y!==o.y||r.s!==o.s)?(this._selId=t.id,this._suppressClick=!0,window.setTimeout(()=>{this._suppressClick=!1},0),this._devicePositionBusy=!0,this.requestUpdate(),this._persistDevicePlacement(t.id,n).then(()=>{const e=this._t("history.device_move",{name:i.displayName});this._devicePositionHistory.push({name:e,before:{deviceId:i.id,spaceId:i.spaceId,placement:i.before},after:{deviceId:i.id,spaceId:i.spaceId,placement:n}})}).catch(e=>{this._previewDevicePlacement(t.id,i.before),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))}).finally(()=>{this._devicePositionBusy=!1,this.requestUpdate()})):this._previewDevicePlacement(t.id,i.before)}_pointerCancel(e,t){this._deviceDrag?.id===t.id&&this._deviceDrag.pointerId===e.pointerId&&this._cancelDeviceDrag()}_showToast(e){for(const e of this.renderRoot.querySelectorAll("hp-dialog"))e.closeTransientOverlays("toast");this._toast=e,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}_syncPointerHoverTargets(){const e=this._pointerModality.hoverEnabled;for(const t of this.renderRoot.querySelectorAll(vf))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(vf)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(vf))e.toggleAttribute("data-pointer-hover",i)}_clearTransientHover(e=!1){e&&this._pointerModality.suspend(),this._tip&&(this._tip=null),this._hoverRoom&&(this._hoverRoom=null),this._syncLiveHover()}_notePointer(e){const t=this._pointerModality.modality,i=this._pointerModality.note(e);"touch"!==i&&"pen"!==i||t===i&&!this._tip&&!this._hoverRoom||this._clearTransientHover()}_guardTouchGesture(e){if(this._editorSecondary?.handleOutsideDismiss(e))return;if("click"===e.type){if(!this._suppressClick&&!this._touchSequenceMultitouch&&Date.now()>this._touchClickBlockUntil)return;return e.preventDefault(),void e.stopImmediatePropagation()}const t=e;if(this._notePointer(t),"pointercancel"===e.type||"lostpointercapture"===e.type?this._doubleFit.clear():"pointerdown"===e.type&&this._doubleFit.clearOutside(t),"touch"===t.pointerType)if("pointerdown"!==e.type){if("pointermove"===e.type){const e=this._touchContacts.get(t.pointerId);if(!e)return;const i={...e,x:t.clientX,y:t.clientY};if(this._touchContacts.set(t.pointerId,i),this._touchSequenceMultitouch&&"view"===this._mode&&!this._vacFit&&this._pinchStart&&[...this._touchContacts.values()].every(e=>e.inStage)){this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=[...this._touchContacts.values()];if(e.length>=2&&this._stageEl){const[t,i]=e,n=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),r=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-r.left,(t.y+i.y)/2-r.top,this._pinchStart.zoom*n),this._saveZoom()}}return}if("pointerup"===e.type||"pointercancel"===e.type||"lostpointercapture"===e.type){if(this._clearTransientHover(),this._touchContacts.delete(t.pointerId),this._touchSequenceMultitouch){if(this._touchClickBlockUntil=Date.now()+500,this._pointers.delete(t.pointerId),!this._vacFit&&this._pointers.size>=2){const[e,t]=[...this._pointers.values()];this._pinchStart={dist:Math.hypot(e.x-t.x,e.y-t.y),zoom:this._zoom}}else this._pinchStart=null;0===this._pointers.size&&(this._panStart=null,this._panLock=null)}0===this._touchContacts.size&&(this._touchSequenceMultitouch=!1)}}else if(this._touchContacts.set(t.pointerId,{x:t.clientX,y:t.clientY,inStage:!!t.target?.closest?.(".stage")}),this._touchContacts.size>=2){this._clearRoomFocus(!0),this._clearTransientHover(),this._touchSequenceMultitouch=!0,this._touchClickBlockUntil=Number.POSITIVE_INFINITY,clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),this._swipeStart=null;const e=[...this._touchContacts.values()];if("view"===this._mode&&!this._vacFit&&e.every(e=>e.inStage)){this._pointers=new Map([...this._touchContacts].map(([e,t])=>[e,{x:t.x,y:t.y}]));const[t,i]=e;this._pinchStart={dist:Math.hypot(t.x-i.x,t.y-i.y),zoom:this._zoom},this._panStart=null,this._panLock=null}else this._vacFit&&(this._pointers.clear(),this._pinchStart=null,this._panStart=null,this._panLock=null)}}_showTip(e,t,i,n,r,o,s=!1){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||this._deviceDrag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:n,temp:r,hum:o,room:s},this._syncLiveHover()))}_roomTipEnabledForPointer(e){return"view"===this._mode&&(this._notePointer(e),!!Ki(this._settings)||(this._tip?.room&&(this._tip=null,this._syncLiveHover()),!1))}get _gridPitch(){return Fa}get _cellCm(){const e=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(e)&&e>0?e:5}_fmtLen(e,t){const i=function(e,t,i,n){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*n}(e,t,this._gridPitch,this._cellCm);return Et(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const e=this._resize?.preview;return e&&e.space===this._space?e.sp:this._serverCfg?.spaces.find(e=>e.id===this._space)}get _renderCfg(){const e=this._resize?.preview;return e&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(t=>t.id===e.space?e.sp:t)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,yf}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return Bt(e?.rooms||[]).map(e=>[e[0]*yf,e[1]*t,e[2]*yf,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(_f)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(_f,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,this._clearRoomFocus(!0),this._cancelDangerConfirm(),"view"!==this._mode&&this._setMode("view",!1),this._pendingNavMode=null,this._geometryHistory.clear(),this._activeDraftId=null,this._resumeDraftBySpace={},this._draftSegmentCms=[],this._closingWallCm=null,this._drawWallField=null,this._showHidden=!1,this._tapConfirm=null,this._vacCalConfirm=null,this._decorEraseConfirm=null,this._openingInfo=null,this._closeInfoCard(),this._rulesDialog=null,this._alignDialog=null,this._preflightClipboardFallback=null,this._backupImportDialog=null,this._backupExportDialog=null,this._supportDialog?.preview?.token&&this._editorRuntime?._discardSupportPreview(this._supportDialog.preview.token),this._supportDialog=null,this._settingsDialog=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){return e!==this._mode&&(this._clearRoomFocus(!0),this._cancelDangerConfirm()),this._warmModeRequest=0,this._editorRuntime?this._editorRuntime._setMode(e,t):"view"===e?void this._editorModeRequest++:void this._requestMode(e,t)}_primeDrawWallField(){return this._editorRuntimeOrThrow()._primeDrawWallField()}get _drawWallFieldValue(){return null===this._drawWallField?uo(15,this._imperial):this._drawWallField}get _drawWallCm(){const e=(e=>{const t=String(e??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t))return null;const i=Number(t);return Number.isFinite(i)?i:null})(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Gs:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Gs:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){return this._editorRuntimeOrThrow()._showPhysicalRange(e,t)}_draftSegmentCount(e=this._curSpaceCfg){return this._editorRuntimeOrThrow()._draftSegmentCount(e)}_mergeSpacePartitions(e,t){return this._editorRuntimeOrThrow()._mergeSpacePartitions(e,t)}_finishWallChain(){return this._editorRuntimeOrThrow()._finishWallChain()}_activateMarkupTool(e){return this._editorRuntimeOrThrow()._activateMarkupTool(e)}_limitReached(e){return this._editorRuntimeOrThrow()._limitReached(e)}_svgPoint(e){return this._editorRuntimeOrThrow()._svgPoint(e)}_snap(e){return this._editorRuntimeOrThrow()._snap(e)}_snapDrawPoint(e,t=!1){return this._editorRuntimeOrThrow()._snapDrawPoint(e,t)}_planSnapOpeningCuts(e,t){return this._editorRuntimeOrThrow()._planSnapOpeningCuts(e,t)}_planSnapGeometrySnapshot(){return this._editorRuntimeOrThrow()._planSnapGeometrySnapshot()}_hiddenWallDiagnosticSnapshot(){return this._editorRuntimeOrThrow()._hiddenWallDiagnosticSnapshot()}_planStructuralGeometrySnapshot(){return this._editorRuntimeOrThrow()._planStructuralGeometrySnapshot()}_planSnapContextKey(e){return this._editorRuntimeOrThrow()._planSnapContextKey(e)}_resolvePlanDrawPoint(e,t){return this._editorRuntimeOrThrow()._resolvePlanDrawPoint(e,t)}get _activePlanSnapCandidate(){if(!this._markup||"draw"!==this._tool)return null;const e=this._planSnapHover;if(!e)return null;const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.candidate:null}get _activePlanSnapConflicts(){if(!this._markup||"draw"!==this._tool)return[];const e=this._planSnapHover;if(!e)return[];const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.conflicts:[]}_clearPlanSnapHover(e=!0){return this._editorRuntime?this._editorRuntimeOrThrow()._clearPlanSnapHover(e):(this._planSnapHover=null,void(e&&(this._cursorPt=null)))}_samePt(e,t){return Xt(e,t)}_dropLegacySegments(){return this._editorRuntimeOrThrow()._dropLegacySegments()}_rollbackRejectedPhysicalWrites(e){return this._editorRuntimeOrThrow()._rollbackRejectedPhysicalWrites(e)}async _reloadRejectedPhysicalWrite(){return this._editorRuntimeOrThrow()._reloadRejectedPhysicalWrite()}get _cfgWriting(){return this._writesPending>0}async _sendConfigCandidate(e){const t=to(e),i=await this.hass.callWS({type:"houseplan/config/set",config:t,expected_rev:this._cfgRev});this._cfgRev=i?.rev??this._cfgRev+1}_writeConfig(){if(this._editorRuntime)return this._editorRuntime._writeConfig();this._writesPending++,this._writeChain=$_(this._writeChain,async()=>{if(!this._serverCfg)return;const e=to(this._serverCfg),t=El(e);t!==El(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t,await this._sendConfigCandidate(e)});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){return this._editorRuntimeOrThrow()._geometrySnapshotFromConfig(e,t)}_geometrySnapshot(e=this._space){return this._editorRuntimeOrThrow()._geometrySnapshot(e)}_recordGeometry(e,t){return this._editorRuntimeOrThrow()._recordGeometry(e,t)}_restoreGeometryStateInConfig(e,t,i=!1){return this._editorRuntimeOrThrow()._restoreGeometryStateInConfig(e,t,i)}_restoreGeometryStateLocal(e){return this._editorRuntimeOrThrow()._restoreGeometryStateLocal(e)}_wallModelBlockerLabel(e){return this._editorRuntimeOrThrow()._wallModelBlockerLabel(e)}_hasLegacyZeroWallFields(e=this._serverCfg){return this._editorRuntimeOrThrow()._hasLegacyZeroWallFields(e)}_showWallModelMigrationBlocked(e){return this._editorRuntimeOrThrow()._showWallModelMigrationBlocked(e)}_limitSegmentsOf(e){return this._editorRuntimeOrThrow()._limitSegmentsOf(e)}_junctionLimitViolations(e,t,i,n){return this._editorRuntimeOrThrow()._junctionLimitViolations(e,t,i,n)}_junctionLimitLabel(e){return this._editorRuntimeOrThrow()._junctionLimitLabel(e)}_junctionLimitsIntroduced(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitsIntroduced(e,t,i)}_commitPhysicalGeometry(e,t,i=[]){return this._editorRuntimeOrThrow()._commitPhysicalGeometry(e,t,i)}_clearGeometryGesture(){return this._editorRuntime?this._editorRuntimeOrThrow()._clearGeometryGesture():(this._path=[],this._planSnapHover=null,this._cursorPt=null,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingPreset=null,this._openingRebindId=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._roomDeleteDialog=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._resize?.reset(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,void(this._bdDrag=null))}_stagePointerCancel(e){if(this._flushHa(),this._editorRuntime?._cancelPointerMove("markup-hover"),this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null),this._doubleFit.clear(),this._editorRuntime)return this._editorRuntime._stagePointerCancel(e);this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._swipeStart=null),this._viewportGestureDirty&&0===this._pointers.size&&(this._viewportGestureDirty=!1,this.requestUpdate())}_applyGeometryState(e,t=!1){return this._editorRuntimeOrThrow()._applyGeometryState(e,t)}_roomAt(e){return this._editorRuntimeOrThrow()._roomAt(e)}_overlapRoom(e){return this._editorRuntimeOrThrow()._overlapRoom(e)}_pointInRoom(e,t){return t.poly?Zt(e,t.poly):null!=t.x&&e[0]>=t.x&&e[0]<=t.x+t.w&&e[1]>=t.y&&e[1]<=t.y+t.h}_contourSelfIntersects(e){return this._editorRuntimeOrThrow()._contourSelfIntersects(e)}_canAppendRoomDraftPoint(){return this._editorRuntimeOrThrow()._canAppendRoomDraftPoint()}_markupClick(e){if(this._editorRuntime)return this._editorRuntime._markupClick(e)}_draftEndAt(e,t){return this._editorRuntimeOrThrow()._draftEndAt(e,t)}_mergeDraftEndpoint(e){return this._editorRuntimeOrThrow()._mergeDraftEndpoint(e)}_adoptDraftCms(e,t,i){return this._editorRuntimeOrThrow()._adoptDraftCms(e,t,i)}_draftSegmentsForPath(e,t,i){return this._editorRuntimeOrThrow()._draftSegmentsForPath(e,t,i)}_persistActiveDraftSegment(){return this._editorRuntimeOrThrow()._persistActiveDraftSegment()}_activeWallSourceKey(e){return this._editorRuntimeOrThrow()._activeWallSourceKey(e)}_wallGraphSources(e){return this._editorRuntimeOrThrow()._wallGraphSources(e)}_wallFaceGraph(e,t){return this._editorRuntimeOrThrow()._wallFaceGraph(e,t)}_offerWallFaces(e,t=this._path.length-2,i){return this._editorRuntimeOrThrow()._offerWallFaces(e,t,i)}_beginWallFaceBatch(e){return this._editorRuntimeOrThrow()._beginWallFaceBatch(e)}_offerExistingWallFace(e){return this._editorRuntimeOrThrow()._offerExistingWallFace(e)}_columnClick(e){return this._editorRuntimeOrThrow()._columnClick(e)}_openPhysicalDialog(e,t,i){return this._editorRuntimeOrThrow()._openPhysicalDialog(e,t,i)}_physicalDown(e,t,i){return this._editorRuntimeOrThrow()._physicalDown(e,t,i)}_clampPhysicalDelta(e,t,i){return this._editorRuntimeOrThrow()._clampPhysicalDelta(e,t,i)}_physicalMove(e){return this._editorRuntimeOrThrow()._physicalMove(e)}_physicalUp(e){return this._editorRuntimeOrThrow()._physicalUp(e)}_registerPhysicalTap(e,t,i){return this._editorRuntimeOrThrow()._registerPhysicalTap(e,t,i)}_cancelPhysicalGesture(){return this._editorRuntimeOrThrow()._cancelPhysicalGesture()}_physicalRotateDown(e,t){return this._editorRuntimeOrThrow()._physicalRotateDown(e,t)}_physicalRotateMove(e){return this._editorRuntimeOrThrow()._physicalRotateMove(e)}_physicalRotateUp(e){return this._editorRuntimeOrThrow()._physicalRotateUp(e)}_rszRooms(){return this._editorRuntimeOrThrow()._rszRooms()}_rszOpenings(){return this._editorRuntimeOrThrow()._rszOpenings()}_rszObstacles(){return this._editorRuntimeOrThrow()._rszObstacles()}_rszOptsFor(e,t){return this._editorRuntimeOrThrow()._rszOptsFor(e,t)}_rszResolution(e,t,i){return this._editorRuntimeOrThrow()._rszResolution(e,t,i)}_rszSnapshot(){return this._editorRuntimeOrThrow()._rszSnapshot()}_rszResetController(){return this._editorRuntimeOrThrow()._rszResetController()}_rszProjectPreview(e,t,i,n,r){return this._editorRuntimeOrThrow()._rszProjectPreview(e,t,i,n,r)}_rszAcceptPreview(e,t){return this._editorRuntimeOrThrow()._rszAcceptPreview(e,t)}_rszSpaceCandidateGeometry(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateGeometry(e,t)}_rszSpaceCandidateRenderable(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateRenderable(e,t)}_rszCandidateRenderable(e){return this._editorRuntimeOrThrow()._rszCandidateRenderable(e)}_rszEdgeDown(e,t,i){return this._editorRuntimeOrThrow()._rszEdgeDown(e,t,i)}_rszReasonText(e){return this._editorRuntimeOrThrow()._rszReasonText(e)}_rszDisabledActivate(e,t){return this._editorRuntimeOrThrow()._rszDisabledActivate(e,t)}_rszDisabledKey(e,t){return this._editorRuntimeOrThrow()._rszDisabledKey(e,t)}_rszMove(e){return this._editorRuntimeOrThrow()._rszMove(e)}_rszUp(e){return this._editorRuntimeOrThrow()._rszUp(e)}_rszCancelDrag(e){return this._editorRuntimeOrThrow()._rszCancelDrag(e)}_rszPointerCancel(e){return this._editorRuntimeOrThrow()._rszPointerCancel(e)}_rszEdgeLabels(e,t,i=this._resize?.rooms){return this._editorRuntimeOrThrow()._rszEdgeLabels(e,t,i)}_rszInnerSpanCms(e,t,i){return this._editorRuntimeOrThrow()._rszInnerSpanCms(e,t,i)}_renderResizeMeasurements(){return this._editorRuntimeOrThrow()._renderResizeMeasurements()}_renderResizeLayer(e,t){return this._editorRuntimeOrThrow()._renderResizeLayer(e,t)}get _openingsR(){const e=this._curSpaceCfg,t=this._spaceH,i=this._spaceModel();return i?(e?.openings||[]).flatMap(e=>{const n={...e,rx:e.x*yf,ry:e.y*t,rlen:e.length*yf};if(!e.host||"wall"===e.host.kind)return[n];const r=Qd(e,i.partitions,yf,this._cellCm,this._gridPitch);if(!r.resolved)return"plan"===this._mode?[{...n,orphanReason:r.reason||"invalid-host"}]:[];let[o,s]=r.resolved.center;const a=this._physicalDrag;return a?.moved&&"partition"===a.kind&&a.id===e.host.id&&(o+=a.delta[0],s+=a.delta[1]),[{...e,rx:o,ry:s,rlen:r.resolved.length,angle:r.resolved.angle,partitionHost:r.resolved}]}):[]}_partitionOpeningCuts(e=this._spaceModel(),t=()=>!0){if(!e)return[];return qp(Bp(this._curSpaceCfg?.id===e.id?this._curSpaceCfg:null,e,this._cellCm,this._gridPitch,yf),t)}_roomWallOpeningInputs(e=this._openingsR,t=this._spaceModel()){if(!t)return[];const i=this._openCuts();return Wp(e,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf)}_openingFace(e,t,i){return e.partitionHost?nu(e.partitionHost,i):Ws(t,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:i})}_cmToUnits(e){return e/this._cellCm*this._gridPitch}get _decorList(){const e=this._curSpaceCfg;return Array.isArray(e?.decor)?e.decor:[]}get _decorH(){return yf}_decorResolvedStyle(e){return function(e,t,i,n=rr){const r="rect"===e?.kind||"ellipse"===e?.kind,o=e,s=Dt(o?.color,n.color);return{color:s,opacity:or(o?.opacity,n.opacity),widthCm:dr(e,t,i,n.widthCm),fill:!!r&&!0===o?.fill,fillColor:Dt(o?.fill_color,o?.fill?s:n.fillColor),fillOpacity:r&&o?.fill?or(o?.fill_opacity,.25):n.fillOpacity}}(e,this._cellCm,this._gridPitch,rr)}_decorWidthUnits(e){return((e,t,i,n=rr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return cr(r,t,i);const o=Number(e?.width);return Number.isFinite(o)&&o>0?o:cr(n,t,i)})(e,this._cellCm,this._gridPitch,rr.widthCm)}_decorTextSizeCm(e){if("text"===e?.kind){const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?t:hr(20*Ei(e),this._cellCm,this._gridPitch)}return hr(20,this._cellCm,this._gridPitch)}_decorTextUnits(e){if("text"!==e.kind)return 20;const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?cr(t,this._cellCm,this._gridPitch):20*Ei(e)}_decorSmallField(e){return Math.round(100*(this._imperial?e/2.54:e))/100}_decorSmallCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(100,t)):.1}_decorTextCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(2e3,t)):.1}_decorLargeField(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_decorLargeCm(e){const t=this._imperial?30.48*e:100*e;return Number.isFinite(t)?Math.max(.1,Math.min(za*this._cellCm,t)):.1}_angleField(e){const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(3))):"0"}_decorBoxOf(e){return"rect"!==e.kind&&"ellipse"!==e.kind&&"furniture"!==e.kind&&"image"!==e.kind?null:{x:e.x*yf,y:e.y*this._decorH,w:e.w*yf,h:e.h*this._decorH,angle:sr(e.angle)||void 0}}_decorSnapGeometry(e){return this._editorRuntimeOrThrow()._decorSnapGeometry(e)}_decorSnap(e,t="mouse",i){return this._editorRuntimeOrThrow()._decorSnap(e,t,i)}_replaceDecor(e,t){return this._editorRuntimeOrThrow()._replaceDecor(e,t)}_cancelDecorGesture(){return this._editorRuntimeOrThrow()._cancelDecorGesture()}_decorPointerDown(e){return this._editorRuntimeOrThrow()._decorPointerDown(e)}_decorCommitDraft(){return this._editorRuntimeOrThrow()._decorCommitDraft()}_decorShapeDown(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDown(e,t)}_decorMoveUpdate(e){this._editorRuntimeOrThrow()._queuePointerMove("decor-move",()=>this._editorRuntimeOrThrow()._decorMoveUpdate(e))}_decorNudge(e,t){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag)return!1;const i=this._decorList.find(e=>e.id===this._decorSel),n=this._curSpaceCfg;if(!i||!n)return!1;const r=function(e,t,i,n,r,o){if(!([t,i,n,r,o].every(Number.isFinite)&&r>0))return null;let s;if(s="line"===e.kind?[[e.x1,e.y1],[e.x2,e.y2]]:"text"===e.kind?[[e.x,e.y]]:mr(e),!s.length||s.some(e=>!e.slice(0,2).every(Number.isFinite)))return null;const a=s.map(e=>e[0]),l=s.map(e=>e[1]),c=Math.min(...a),h=Math.max(...a),d=Math.min(...l),u=Math.max(...l),p=t/n,m=i/r,_=p>0?Math.min(p,Math.max(0,o-h)):Math.max(p,Math.min(0,-o-c)),g=m>0?Math.min(m,Math.max(0,o-u)):Math.max(m,Math.min(0,-o-d));return _||g?"line"===e.kind?{...e,x1:e.x1+_,y1:e.y1+g,x2:e.x2+_,y2:e.y2+g}:{...e,x:e.x+_,y:e.y+g}:null}(i,e,t,yf,this._decorH,za);if(!r)return!1;const o=this._geometrySnapshot();return n.decor=this._decorList.map(e=>e.id===i.id?r:e),this._recordGeometry(this._t("history.decor_move"),o),this._saveConfig(),this.requestUpdate(),!0}_decorShapeDbl(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDbl(e,t)}_openDecorProperties(e){return this._editorRuntimeOrThrow()._openDecorProperties(e)}_decorOpenText(e){return this._editorRuntimeOrThrow()._decorOpenText(e)}_decorRememberTextSelection(e){return this._editorRuntimeOrThrow()._decorRememberTextSelection(e)}_decorInsertLiveVariable(e){return this._editorRuntimeOrThrow()._decorInsertLiveVariable(e)}_decorSaveText(){return this._editorRuntimeOrThrow()._decorSaveText()}_decorSaveShape(){return this._editorRuntimeOrThrow()._decorSaveShape()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(e=>e.id===this._decorSel)||null}_dtPivot(e){return this._editorRuntimeOrThrow()._dtPivot(e)}_dtApply(e,t){return this._editorRuntimeOrThrow()._dtApply(e,t)}_dtStart(e,t,i,n){return this._editorRuntimeOrThrow()._dtStart(e,t,i,n)}_dtMove(e){this._editorRuntimeOrThrow()._queuePointerMove("decor-transform",()=>this._editorRuntimeOrThrow()._dtMove(e))}_dtUp(){return this._editorRuntimeOrThrow()._flushPointerMove("decor-transform"),this._editorRuntimeOrThrow()._dtUp()}_dtMeasure(){return this._editorRuntimeOrThrow()._dtMeasure()}_deleteDecor(e){return this._editorRuntimeOrThrow()._deleteDecor(e)}_decorDeleteSel(){return this._editorRuntimeOrThrow()._decorDeleteSel()}_confirmDecorErase(){return this._editorRuntimeOrThrow()._confirmDecorErase()}get _bdBase(){const e=this._curSpaceCfg;return e?.plan_url?{...$a(e.plan_aspect,yf)}:null}get _bdRect(){const e=this._curSpaceCfg;return e?.plan_url?Ra(e,yf):null}get _bdParams(){const e=this._curSpaceCfg,t=Number(e?.plan_x),i=Number(e?.plan_y),n=Number(e?.plan_scale),r=Number(e?.plan_scale_x),o=Number(e?.plan_scale_y),s=Number.isFinite(n)&&n>0?n:1;return{dx:Number.isFinite(t)?t:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(r)&&r>0?r:s,sy:Number.isFinite(o)&&o>0?o:s,angle:sr(e?.plan_angle)}}_openBackdropDialog(e){if(this._editorRuntime)return this._editorRuntime._openBackdropDialog(e)}_saveBackdropDialog(){return this._editorRuntimeOrThrow()._saveBackdropDialog()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(e,t,i,n,r){return this._editorRuntimeOrThrow()._bdApply(e,t,i,n,r)}_bdStart(e,t,i=!1){return this._editorRuntimeOrThrow()._bdStart(e,t,i)}_bdMove(e){return this._editorRuntimeOrThrow()._bdMove(e)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const e=this._bdParams;return 0!==e.dx||0!==e.dy||1!==e.sx||1!==e.sy||0!==e.angle}_bdReset(){return this._editorRuntimeOrThrow()._bdReset()}_bdUp(){return this._editorRuntimeOrThrow()._bdUp()}get _bdLive(){if(!this._bdDrag)return null;const e=this._bdRect;return e?{x:e.x+e.w/2,y:e.y+e.h/2,text:`${this._fmtLen([0,0],[e.w,0])} × ${this._fmtLen([0,0],[0,e.h])}`}:null}_renderBackdropFrame(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderBackdropFrame(e):V}_renderTextFrame(e){const t=this._dtSel;if(!t)return V;if("text"===t.kind&&this._dtBox?.id!==t.id)return V;const i="text"===t.kind?this._dtBox:this._decorBoxOf(t);if("line"!==t.kind&&!i)return V;const n=.018*Math.max(e.w,e.h),r=n/4;if("line"===t.kind){const e=[t.x1*yf,t.y1*this._decorH],i=[t.x2*yf,t.y2*this._decorH];return j` ${[e,i].map((e,t)=>j`this._dtStart(e,"scale",void 0,t)}> `)} - `}const[o,s]=this._dtPivot(t),a=Number(t.angle)||0,l=[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]],c=2.2*n,h="furniture"===t.kind||"image"===t.kind,d=h?[[0,-1,"ns"],[1,0,"ew"],[0,1,"ns"],[-1,0,"ew"]]:[];return j` + `}if(!i)return V;const[o,s]=this._dtPivot(t),a=Number(t.angle)||0,l=[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]],c=2.2*n,h="furniture"===t.kind||"image"===t.kind,d=h?[[0,-1,"ns"],[1,0,"ew"],[0,1,"ns"],[-1,0,"ew"]]:[];return j` this._dtStart(e,"rotate")}> - ${wf.map(e=>"edges"===e?d.map(([e,t,o])=>{const s=e<0?i.x:e>0?i.x+i.w:i.x+i.w/2,a=t<0?i.y:t>0?i.y+i.h:i.y+i.h/2;return j`"edges"===e?d.map(([e,t,o])=>{const s=e<0?i.x:e>0?i.x+i.w:i.x+i.w/2,a=t<0?i.y:t>0?i.y+i.h:i.y+i.h/2;return j`this._dtStart(i,"scale",[e,t])}> `}):l.map(([e,t,o])=>j`this._dtStart(i,"scale",[e,t])}>`))} - `}_renderDecorLayer(){const e=_f,t=this._decorH,i="decor"===this._mode,n=i&&"erase"===this._decorTool,r=this._stageEl,o=this._viewOr(this._baseVb()),s="iso"===this._renderProjection?1:function(e,t,i,n){const r=Number(e),o=Number(t),s=Number(i),a=Number(n);if(![r,o,s,a].every(e=>Number.isFinite(e)&&e>0))return 1;const l=Math.min(r/s,o/a);return Number.isFinite(l)&&l>0?l:1}(r?.clientWidth,r?.clientHeight,o.w,o.h),a=this._decorList.map(r=>{const o="dshape"+(i&&this._decorSel===r.id?" dsel":""),a=this._decorResolvedStyle(r),l=this._decorWidthUnits(r),c=e=>this._decorShapeDown(e,r),h=e=>this._decorShapeDbl(e,r);if("line"===r.kind)return j``}_renderDecorLayer(e){const t=yf,i=this._decorH,n="decor"===this._mode,r=n&&"erase"===this._decorTool,o=this._stageEl,s=this._viewOr(this._baseVb()),a="iso"===this._renderProjection?1:function(e,t,i,n){const r=Number(e),o=Number(t),s=Number(i),a=Number(n);if(![r,o,s,a].every(e=>Number.isFinite(e)&&e>0))return 1;const l=Math.min(r/s,o/a);return Number.isFinite(l)&&l>0?l:1}(o?.clientWidth,o?.clientHeight,s.w,s.h),l=this._decorList.filter(t=>void 0===e||t.id===e).map(e=>{const o="dshape"+(n&&this._decorSel===e.id?" dsel":""),s=this._decorResolvedStyle(e),l=this._decorWidthUnits(e),c=t=>this._decorShapeDown(t,e),h=t=>this._decorShapeDbl(t,e);if("line"===e.kind)return j` - ${i&&"select"===this._decorTool?j``:V} - ${n?j``:V}`;if("rect"===r.kind){const i=(r.x+r.w/2)*e,s=(r.y+r.h/2)*t,d=sr(r.angle);return j``:V}`;if("rect"===e.kind){const n=(e.x+e.w/2)*t,a=(e.y+e.h/2)*i,d=sr(e.angle);return j` - ${n?j``:V}`}if("ellipse"===r.kind){const i=(r.x+r.w/2)*e,s=(r.y+r.h/2)*t,d=sr(r.angle);return j``:V}`}if("ellipse"===e.kind){const n=(e.x+e.w/2)*t,a=(e.y+e.h/2)*i,d=sr(e.angle);return j` - ${n?j``:V}`}if("image"===r.kind){const s=rm(r,e,t);if(!s)return V;const[a,l,d,u,p,m]=s,_=this._decorAssets.get(r.asset_id);if(!_)return i&&this._editorRuntime?this._editorRuntime._renderMissingDecorImage(r,o,m,a,l,d,u,c,h):V;const g=this._display(_.url);return g?j``:V}`}if("image"===e.kind){const s=sm(e,t,i);if(!s)return V;const[a,l,d,u,p,m]=s,_=this._decorAssets.get(e.asset_id);if(!_)return n&&this._editorRuntime?this._editorRuntime._renderMissingDecorImage(e,o,m,a,l,d,u,c,h):V;const g=this._display(_.url);return g?j`this._signer.markLoaded(this._renderPlanHass,_.url,g)} @pointerdown=${c} @dblclick=${h}> - ${n?j``:V}`:V}if("furniture"===r.kind){const d=kl(r.symbol);if(!d)return V;const u=Cl(r,e,t,d.viewW,d.viewH),p=xl(l,s),m=xl(l+cr(20,this._cellCm,this._gridPitch),s);return j``:V}`:V}if("furniture"===e.kind){const d=xl(e.symbol);if(!d)return V;const u=Rl(e,t,i,d.viewW,d.viewH),p=Sl(l,a),m=Sl(l+cr(20,this._cellCm,this._gridPitch),a);return j` - ${i&&"select"===this._decorTool?j``:V} - ${n?j``:V}`}if("text"===r.kind){const i=this._decorTextUnits(r),n=this._renderDeviceSnapshot?.facts.get(`decor:${this._space}:${r.id}`),s=function(e){return String(e??"").replace(/\r\n?/g,"\n").split("\n")}("string"==typeof n?n:Oi(r.text,r,this._renderPlanHass,e=>this._renderEntityAvailable(e))),l=r.x*e,d=r.y*t,u=Number(r.angle)||0,p=d-(s.length-1)*i*1.2/2;return j`${s.map((e,t)=>j`${e}`)}`}return V});let l=V;const c=this._decorDraft;if(c){const e=this._decorStyle,t=cr(e.widthCm,this._cellCm,this._gridPitch);if("line"===c.kind)l=j``;else{const i=Math.min(c.a[0],c.b[0]),n=Math.min(c.a[1],c.b[1]),r=Math.abs(c.b[0]-c.a[0]),o=Math.abs(c.b[1]-c.a[1]);l="rect"===c.kind?j``:V}`}if("text"===e.kind){const n=this._decorTextUnits(e),r=this._renderDeviceSnapshot?.facts.get(`decor:${this._space}:${e.id}`),a=function(e){return String(e??"").replace(/\r\n?/g,"\n").split("\n")}("string"==typeof r?r:Oi(e.text,e,this._renderPlanHass,e=>this._renderEntityAvailable(e))),l=e.x*t,d=e.y*i,u=Number(e.angle)||0,p=d-(a.length-1)*n*1.2/2;return j`${a.map((e,t)=>j`${e}`)}`}return V});let c=V;const h=this._decorDraft;if(h){const e=this._decorStyle,t=cr(e.widthCm,this._cellCm,this._gridPitch);if("line"===h.kind)c=j``;else{const i=Math.min(h.a[0],h.b[0]),n=Math.min(h.a[1],h.b[1]),r=Math.abs(h.b[0]-h.a[0]),o=Math.abs(h.b[1]-h.a[1]);c="rect"===h.kind?j``:j``}}return j`${a}${l}${this._editorRuntime?._renderFurniturePlacementPreview(s)??V}${this._editorRuntime?._renderDecorImagePlacementPreview()??V}`}get _editorToolbarGroups(){return"plan"!==this._mode?[]:[{id:"opening",label:this._t("markup.opening"),icon:"mdi:door",activeItemId:"opening"===this._tool?this._openingPreset?.type:void 0,items:[{id:"window",label:this._t("opening.window"),icon:"mdi:window-closed-variant",role:"tool",invoke:()=>this._activateOpeningPlacement("window")},{id:"door",label:this._t("opening.door"),icon:"mdi:door-open",role:"tool",invoke:()=>this._activateOpeningPlacement("door")},{id:"passage",label:this._t("opening.passage"),icon:"mdi:arch",role:"tool",invoke:()=>this._activateOpeningPlacement("passage")},{id:"gate",label:this._t("opening.gate"),icon:"mdi:gate",role:"tool",invoke:()=>this._activateOpeningPlacement("gate")}]}]}_renderEditorGroupLauncher(e){return this._editorRuntimeOrThrow()._renderEditorGroupLauncher(e)}get _editorSecondaryContextId(){const e=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,t=this._editorSecondary?.activeGroup(this._editorToolbarGroups);if(t)return`${e}:group:${t.id}:${this._editorSecondary?.groupGeneration}`;if("plan"===this._mode){const t=this._physicalSel;return t?`${e}:selection:${t.kind}:${t.id}:${t.segment??""}`:`${e}:tool:${this._tool}:${this._path.length}`}return"decor"===this._mode?"furniture"===this._decorTool?`${e}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"image"===this._decorTool?`${e}:palette:image:${this._decorImagePalette?.asset_id||"none"}`:"select"===this._decorTool&&this._decorSel?`${e}:selection:decor:${this._decorSel}`:`${e}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${e}:none`}_runEditorContext(e,t){return this._editorRuntimeOrThrow()._runEditorContext(e,t)}_renderEditorGroupModel(e){return this._editorRuntimeOrThrow()._renderEditorGroupModel(e)}_renderDrawWallControl(){return this._editorRuntimeOrThrow()._renderDrawWallControl()}_renderPlanSecondary(){return this._editorRuntimeOrThrow()._renderPlanSecondary()}_renderDecorSecondary(){return this._editorRuntimeOrThrow()._renderDecorSecondary()}_withBackdropReset(e){return this._editorRuntimeOrThrow()._withBackdropReset(e)}get _editorSecondaryDialogBlocked(){return!!(this._dangerConfirm||this._tapConfirm||this._vacCalConfirm||this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._openingInfo||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog||this._markerDialog||this._deviceInbox||this._infoCard||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._backupExportDialog||this._backupImportDialog||this._wallDialog)}_renderEditorSecondary(){return this._editorRuntimeOrThrow()._renderEditorSecondary()}_renderDecorBar(){return this._editorRuntimeOrThrow()._renderDecorBar()}_renderDecorEraseConfirm(){return this._editorRuntimeOrThrow()._renderDecorEraseConfirm()}_renderDecorTextDialog(){return this._editorRuntimeOrThrow()._renderDecorTextDialog()}_renderDecorShapeDialog(){return this._editorRuntimeOrThrow()._renderDecorShapeDialog()}_renderBackdropDialog(){return this._editorRuntimeOrThrow()._renderBackdropDialog()}_cssPxToRender(e){return this._editorRuntimeOrThrow()._cssPxToRender(e)}_renderZeroWalls(e){if(e&&!e.showBorders&&!this._editing)return j``;const t=this._zeroWalls();if(!t.lines.length)return j``;const i=e?.color||"var(--hp-muted)";return j``}}return j`${l}${c}${this._editorRuntime?._renderFurniturePlacementPreview(a)??V}${this._editorRuntime?._renderDecorImagePlacementPreview()??V}`}get _editorToolbarGroups(){return"plan"!==this._mode?[]:[{id:"opening",label:this._t("markup.opening"),icon:"mdi:door",activeItemId:"opening"===this._tool?this._openingPreset?.type:void 0,items:[{id:"window",label:this._t("opening.window"),icon:"mdi:window-closed-variant",role:"tool",invoke:()=>this._activateOpeningPlacement("window")},{id:"door",label:this._t("opening.door"),icon:"mdi:door-open",role:"tool",invoke:()=>this._activateOpeningPlacement("door")},{id:"passage",label:this._t("opening.passage"),icon:"mdi:arch",role:"tool",invoke:()=>this._activateOpeningPlacement("passage")},{id:"gate",label:this._t("opening.gate"),icon:"mdi:gate",role:"tool",invoke:()=>this._activateOpeningPlacement("gate")}]}]}_renderEditorGroupLauncher(e){return this._editorRuntimeOrThrow()._renderEditorGroupLauncher(e)}get _editorSecondaryContextId(){const e=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,t=this._editorSecondary?.activeGroup(this._editorToolbarGroups);if(t)return`${e}:group:${t.id}:${this._editorSecondary?.groupGeneration}`;if("plan"===this._mode){const t=this._physicalSel;return t?`${e}:selection:${t.kind}:${t.id}:${t.segment??""}`:`${e}:tool:${this._tool}:${this._path.length}`}return"decor"===this._mode?"furniture"===this._decorTool?`${e}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"image"===this._decorTool?`${e}:palette:image:${this._decorImagePalette?.asset_id||"none"}`:"select"===this._decorTool&&this._decorSel?`${e}:selection:decor:${this._decorSel}`:`${e}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${e}:none`}_runEditorContext(e,t){return this._editorRuntimeOrThrow()._runEditorContext(e,t)}_renderEditorGroupModel(e){return this._editorRuntimeOrThrow()._renderEditorGroupModel(e)}_renderDrawWallControl(){return this._editorRuntimeOrThrow()._renderDrawWallControl()}_renderPlanSecondary(){return this._editorRuntimeOrThrow()._renderPlanSecondary()}_renderDecorSecondary(){return this._editorRuntimeOrThrow()._renderDecorSecondary()}_withBackdropReset(e){return this._editorRuntimeOrThrow()._withBackdropReset(e)}get _editorSecondaryDialogBlocked(){return!!(this._dangerConfirm||this._tapConfirm||this._vacCalConfirm||this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._openingInfo||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog||this._markerDialog||this._deviceInbox||this._infoCard||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._backupExportDialog||this._backupImportDialog||this._wallDialog)}_renderEditorSecondary(){return this._editorRuntimeOrThrow()._renderEditorSecondary()}_renderDecorBar(){return this._editorRuntimeOrThrow()._renderDecorBar()}_renderDecorEraseConfirm(){return this._editorRuntimeOrThrow()._renderDecorEraseConfirm()}_renderDecorTextDialog(){return this._editorRuntimeOrThrow()._renderDecorTextDialog()}_renderDecorShapeDialog(){return this._editorRuntimeOrThrow()._renderDecorShapeDialog()}_renderBackdropDialog(){return this._editorRuntimeOrThrow()._renderBackdropDialog()}_cssPxToRender(e){return this._editorRuntimeOrThrow()._cssPxToRender(e)}_renderZeroWalls(e){if(e&&!e.showBorders&&!this._editing)return j``;const t=this._zeroWalls();if(!t.lines.length)return j``;const i=e?.color||"var(--hp-muted)";return j` ${t.lines.map(e=>j``)} - `}_zeroWalls(){const e=this._spaceModel();return e?Fp(this._curSpaceCfg,e,_f,.02*this._gridPitch):{style:Dp(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){return this._editorRuntimeOrThrow()._deleteRoomClick(e)}get _wallKeyPitch(){return Fa}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_intervalCm(e){const t=this._spaceModel();return t?ms(t.rooms,this._spaceWalls,this._openCuts(),e,this._wallKeyPitch,this._cellCm,this._gridPitch,_f):0}_normalizeWalls(e,t){const i=this._spaceModel();if(!i)return[];return Co(ps(i.rooms,e,t,this._wallKeyPitch,this._cellCm,this._gridPitch,_f),this._curSpaceCfg?.rooms||[],Fa,1,t.map(e=>[e[0]/_f,e[1]/_f,e[2]/_f,e[3]/_f]))}_paperShapes(e){if(!this._spaceWalls.length)return Lt(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:Lt(e)}_wallUnionGeometry(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._physicalBodiesR();if(!t.length&&!i.length)return null;const n=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==n){const r=_g(this._wallUnionPool,n);if(r.hit)this._wallUnionCache=r.value;else{const r=this._openCuts(),o=this._roomWallOpeningInputs(),s=Fs(e.rooms,t,r,o,this._wallKeyPitch,this._cellCm,this._gridPitch,_f,i);s&&Object.defineProperty(s,"sourceFingerprint",{value:Il([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const a={key:n,value:s};gg(this._wallUnionPool,n,a,8),this._wallUnionCache=a}}return this._wallUnionCache.value}_thickWallCuts(){const e=this._spaceModel();if(!e)return[];const t=this._spaceWalls;if(!t.length)return[];const i=this._openCuts();return Is(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,r=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),s=`${e.id}|${this._cfgEpoch}|${t}|${o}`,a=_g(this._innerContourCache,s);if(a.hit)return a.value;const l=fs(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f,n,r);return gg(this._innerContourCache,s,l,600),l}_wallThickHit(e){return this._editorRuntimeOrThrow()._wallThickHit(e)}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const e=this._wallThickHit(this._cursorPt);if(!e)return null;const t=function(e,t,i){const n=Number.isFinite(e)&&e>0?e:0,r=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return n>0?po(n,r,o)/2:ya(1.5*o,r)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const n of e.segs)i+=(i?" ":"")+Wo([[n[0],n[1]],[n[2],n[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){return this._editorRuntimeOrThrow()._wallThickClick(e)}_wallThickApply(e){return this._editorRuntimeOrThrow()._wallThickApply(e)}_wallHatchDefs(e){const t=lo(this._cellCm);return j` + `}_zeroWalls(){const e=this._spaceModel();return e?Ip(this._curSpaceCfg,e,yf,.02*this._gridPitch):{style:Ap(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){return this._editorRuntimeOrThrow()._deleteRoomClick(e)}get _wallKeyPitch(){return Oa}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_intervalCm(e){const t=this._spaceModel();return t?ms(t.rooms,this._spaceWalls,this._openCuts(),e,this._wallKeyPitch,this._cellCm,this._gridPitch,yf):0}_normalizeWalls(e,t){const i=this._spaceModel();if(!i)return[];return Co(ps(i.rooms,e,t,this._wallKeyPitch,this._cellCm,this._gridPitch,yf),this._curSpaceCfg?.rooms||[],Oa,1,t.map(e=>[e[0]/yf,e[1]/yf,e[2]/yf,e[3]/yf]))}_paperShapes(e){if(!this._spaceWalls.length)return Lt(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:Lt(e)}_wallUnionGeometry(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._physicalBodiesR();if(!t.length&&!i.length)return null;const n=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==n){const r=yg(this._wallUnionPool,n);if(r.hit)this._wallUnionCache=r.value;else{const r=this._openCuts(),o=this._roomWallOpeningInputs(),s=Fs(e.rooms,t,r,o,this._wallKeyPitch,this._cellCm,this._gridPitch,yf,i);s&&Object.defineProperty(s,"sourceFingerprint",{value:El([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const a={key:n,value:s};bg(this._wallUnionPool,n,a,8),this._wallUnionCache=a}}return this._wallUnionCache.value}_thickWallCuts(){const e=this._spaceModel();if(!e)return[];const t=this._spaceWalls;if(!t.length)return[];const i=this._openCuts();return Is(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,r=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),s=`${e.id}|${this._cfgEpoch}|${t}|${o}`,a=yg(this._innerContourCache,s);if(a.hit)return a.value;const l=fs(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf,n,r);return bg(this._innerContourCache,s,l,600),l}_wallThickHit(e){return this._editorRuntimeOrThrow()._wallThickHit(e)}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const e=this._wallThickHit(this._cursorPt);if(!e)return null;const t=function(e,t,i){const n=Number.isFinite(e)&&e>0?e:0,r=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return n>0?po(n,r,o)/2:ba(1.5*o,r)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const n of e.segs)i+=(i?" ":"")+Wo([[n[0],n[1]],[n[2],n[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){return this._editorRuntimeOrThrow()._wallThickClick(e)}_wallThickApply(e){return this._editorRuntimeOrThrow()._wallThickApply(e)}_wallHatchDefs(e){const t=lo(this._cellCm);return j` - `}_resolvedRoomFills(e,t){const i=new Map,n=new Map;for(const r of e.rooms){const e=this._roomDialog&&r.id===this._roomEditId?this._roomFill||t.fill:yn(t.fill,r),o=this._roomDialog&&r.id===this._roomEditId?this._roomCustomFill||t.customFill:Ui(t.customFill,r),s=Ji(e,"lqi"===e&&r.area?this._roomLqi(r.area):null,"light"===e?Kh(Vh(this._renderPlanHass,this._renderDevices,r,this._virtualLights)):"none","temp"===e?this._roomTemp(r):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(r,s),r.id&&n.set(r.id,s)}return{byRoom:i,byId:n}}_spaceDisplayForRender(){const e=Vi(this._curSpaceCfg),t=this._spaceDialog;return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,showBorders:t.showBorders,showNames:t.showNames,hideDecor:t.hideDecor,hideOpenings:t.hideOpenings,color:t.roomColor,opacity:t.roomOpacity,fill:t.fillMode,customFill:t.customFill?ji(t.customFill):Wi,glow:t.glowEnabled,tempMin:t.tempMin,tempMax:t.tempMax,showLqi:t.showLqi,cardFontScale:t.cardFontScale,labelTemp:t.labelTemp,labelHum:t.labelHum,labelLqi:t.labelLqi,labelLight:t.labelLight}:e}_openingWallIndexFor(e,t){const i=e.rooms.map(e=>`${e.id}:${e.poly?.map(e=>e.join(",")).join("/")||`${e.x},${e.y},${e.w},${e.h}`}`).join(";"),n=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),r=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,n,r].join("|");let s=this._openingWallIndexCache.get(o);return s||(s=Ns(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,_f)),gg(this._openingWallIndexCache,o,s,4),{key:o,value:s}}_renderOpeningTunnelFills(e,t,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return j``;if("glow-base"===i&&![...t.byRoom.values()].some(Boolean))return j``;const n=this._openCuts(),r=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=r.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),s=this._openingWallIndexFor(e,n),a=`${s.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===a||(this._openingTunnelCache={key:a,value:Us(s.value,r)}),yp({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:t.byId,idPrefix:`${e.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(e,t,i){const n=new Map,r=new Map,o=this._fillColors.glow_base;for(const s of e.rooms){const e=i.byRoom.get(s),a=bn(t.glow,s)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;n.set(s,a),s.id&&r.set(s.id,a)}return{byRoom:n,byId:r}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,Ht(e)])),n=this._openCuts(),r=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",s=e.rooms.map(s=>{const a=t.byRoom.get(s)||null,l=i.get(s)||null;if(!a||!l)return V;const c=this._spaceWalls.length&&s.id&&this._innerRoomContour(e,s.id,n,r)||l,h=ci(c,e.rooms.filter(e=>e!==s).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(s,c,e).path;return d||h.length?j``}_resolvedRoomFills(e,t){const i=new Map,n=new Map;for(const r of e.rooms){const e=this._roomDialog&&r.id===this._roomEditId?this._roomFill||t.fill:yn(t.fill,r),o=this._roomDialog&&r.id===this._roomEditId?this._roomCustomFill||t.customFill:Ui(t.customFill,r),s=Ji(e,"lqi"===e&&r.area?this._roomLqi(r.area):null,"light"===e?Xh(Kh(this._renderPlanHass,this._renderDevices,r,this._virtualLights)):"none","temp"===e?this._roomTemp(r):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(r,s),r.id&&n.set(r.id,s)}return{byRoom:i,byId:n}}_spaceDisplayForRender(){const e=Vi(this._curSpaceCfg),t=this._spaceDialog;return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,showBorders:t.showBorders,showNames:t.showNames,hideDecor:t.hideDecor,hideOpenings:t.hideOpenings,color:t.roomColor,opacity:t.roomOpacity,fill:t.fillMode,customFill:t.customFill?ji(t.customFill):Wi,glow:t.glowEnabled,tempMin:t.tempMin,tempMax:t.tempMax,showLqi:t.showLqi,cardFontScale:t.cardFontScale,labelTemp:t.labelTemp,labelHum:t.labelHum,labelLqi:t.labelLqi,labelLight:t.labelLight}:e}_openingWallIndexFor(e,t){const i=e.rooms.map(e=>`${e.id}:${e.poly?.map(e=>e.join(",")).join("/")||`${e.x},${e.y},${e.w},${e.h}`}`).join(";"),n=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),r=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,n,r].join("|");let s=this._openingWallIndexCache.get(o);return s||(s=Hs(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,yf)),bg(this._openingWallIndexCache,o,s,4),{key:o,value:s}}_renderOpeningTunnelFills(e,t,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return j``;if("glow-base"===i&&![...t.byRoom.values()].some(Boolean))return j``;const n=this._openCuts(),r=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=r.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),s=this._openingWallIndexFor(e,n),a=`${s.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===a||(this._openingTunnelCache={key:a,value:Vs(s.value,r)}),wp({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:t.byId,idPrefix:`${e.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(e,t,i){const n=new Map,r=new Map,o=this._fillColors.glow_base;for(const s of e.rooms){const e=i.byRoom.get(s),a=bn(t.glow,s)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;n.set(s,a),s.id&&r.set(s.id,a)}return{byRoom:n,byId:r}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,Ht(e)])),n=this._openCuts(),r=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",s=e.rooms.map(s=>{const a=t.byRoom.get(s)||null,l=i.get(s)||null;if(!a||!l)return V;const c=this._spaceWalls.length&&s.id&&this._innerRoomContour(e,s.id,n,r)||l,h=ci(c,e.rooms.filter(e=>e!==s).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(s,c,e).path;return d||h.length?j``:s.poly||c!==l?j``)} - `}_isoDecorationLayers(e){const t="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(e){const t=!!e.showBorders;return{structural:t,panels:t&&!e.hideOpenings,shadows:t&&e.filtersSupported&&!e.forcedColors,materialNuance:t&&!e.forcedColors,floorSymbols:!e.hideOpenings&&!t}}({showBorders:e.showBorders,hideOpenings:e.hideOpenings,filtersSupported:t,forcedColors:i})}_isoOpeningPanels(e){if(!e.panels)return[];const t=this._isoScene();if(!t)return[];return t.openings.flatMap(e=>{const t=this._openingsR[e.sourceIndex];return t?Wg(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=va(this._cellCm);return j` + `}_isoDecorationLayers(e){const t="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(e){const t=!!e.showBorders;return{structural:t,panels:t&&!e.hideOpenings,shadows:t&&e.filtersSupported&&!e.forcedColors,materialNuance:t&&!e.forcedColors,floorSymbols:!e.hideOpenings&&!t}}({showBorders:e.showBorders,hideOpenings:e.hideOpenings,filtersSupported:t,forcedColors:i})}_isoOpeningPanels(e){if(!e.panels)return[];const t=this._isoScene();if(!t)return[];return t.openings.flatMap(e=>{const t=this._openingsR[e.sourceIndex];return t?Gg(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=ya(this._cellCm);return j` ${"walls"===t&&e.materialNuance?j` @@ -5079,7 +5079,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef `}_renderIsoUnderlay(e){if(!e.structural)return j``;const t=this._isoScene()?.floor;return t?j``:j``}_renderIsoShadows(e,t){if(!e.shadows)return j``;const i=this._isoScene()?.geometry;return i?j``:j``}_roomHoverPaths(e){const t=this._hoverRoom;if("view"!==this._mode||!t||t.space!==e.id)return null;const i=e.rooms.find(e=>e===t.room||!!e.id&&e.id===t.room.id);if(!i)return null;const n=Ht(i);if(!n)return null;const r=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:Ht(e)})).filter(e=>!!e.poly),o=ci(n,r.map(e=>e.poly)),s=this._openCuts(),a=.02*this._gridPitch,l=s.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return n.some((e,i)=>Mn(t,[e[0],e[1],...n[(i+1)%n.length]])<=4*a)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,s,h)||n,u=[{axis:n,face:d}];for(const t of o){const i=r.find(e=>e.poly===t)?.room;let n=t;if(c.length&&i?.id){const r=hs(e.rooms,i.id,c,s,this._wallKeyPitch,this._cellCm,this._gridPitch,_f);r&&(n=Es(r.poly,r.offsets)||t)}u.push({axis:t,face:n})}const p=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,n=Math.sin(t)*e.length/2;return[e.x-i,e.y-n,e.x+i,e.y+n]}),m=l.concat(p),_=u.map(({axis:e,face:t})=>{const i=m.map(i=>((e,t,i)=>{const n=e[2]-e[0],r=e[3]-e[1],o=Math.hypot(n,r);if(o.05)&&Mn([c,h],[i[0],i[1],n[0],n[1]])<=4*a){d=!0;break}}if(!d)return null;let u=null;for(let e=0;e.05)continue;const p=Mn([c,h],[t[0],t[1],n[0],n[1]]);(!u||p!!e);return i.length?mn(t,i,a).map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" "):`M ${t.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!_)return null;const g=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,e).path||g(d),...o.map(g)].join(" "),outlineD:_}}_renderRoomHoverFill(e){return e?j` - - `:j``}_renderRoomHoverOutline(e){return e?j` - - - `:j``}_renderWallThickUi(){return this._editorRuntime?this._editorRuntimeOrThrow()._renderWallThickUi():j``}_renderWallThickDialog(){return this._editorRuntimeOrThrow()._renderWallThickDialog()}_openingAt(e){return this._editorRuntimeOrThrow()._openingAt(e)}_resolveOpeningPlacement(e){return this._editorRuntimeOrThrow()._resolveOpeningPlacement(e)}_activateOpeningPlacement(e){return this._editorRuntimeOrThrow()._activateOpeningPlacement(e)}_clearOpeningPlacement(e){return this._editorRuntime?this._editorRuntimeOrThrow()._clearOpeningPlacement(e):(this._openingHoverCandidate=null,this._openingJambBlockCm=null,void(e&&(this._openingPreset=null,this._openingRebindId=null)))}_openingClick(e){return this._editorRuntimeOrThrow()._openingClick(e)}_editOpening(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._editOpening(e)}_opPointerDown(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerDown(e,t)}_opPointerMove(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerMove(e,t)}_opRuler(e,t){return this._editorRuntimeOrThrow()._opRuler(e,t)}_opPointerUp(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerUp(e,t)}_opClick(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opClick(e,t)}_saveOpening(){return this._editorRuntimeOrThrow()._saveOpening()}_deleteOpening(){return this._editorRuntimeOrThrow()._deleteOpening()}_contactCandidates(){return this._editorRuntimeOrThrow()._contactCandidates()}_lockCandidates(){return this._editorRuntimeOrThrow()._lockCandidates()}_toggleOpeningEntityPicker(e){return this._editorRuntimeOrThrow()._toggleOpeningEntityPicker(e)}_filterOpeningEntities(e,t){return this._editorRuntimeOrThrow()._filterOpeningEntities(e,t)}_selectOpeningEntity(e,t){return this._editorRuntimeOrThrow()._selectOpeningEntity(e,t)}_mergeClick(e){return this._editorRuntimeOrThrow()._mergeClick(e)}_commitMerge(){return this._editorRuntimeOrThrow()._commitMerge()}_splitClick(e){return this._editorRuntimeOrThrow()._splitClick(e)}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])&&(null!=this._closingWallCm||!this._activeDraftId)}_markupMove(e){if(this._markup&&this._editorRuntime)return this._editorRuntime._markupMove(e)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&Jg(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const n=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=n,n}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){return this._editorRuntimeOrThrow()._saveRoom()}_decideWallFace(e){return this._editorRuntimeOrThrow()._decideWallFace(e)}_wallSourceCmAt(e,t,i){return this._editorRuntimeOrThrow()._wallSourceCmAt(e,t,i)}_activePathWithRepair(e,t){return this._editorRuntimeOrThrow()._activePathWithRepair(e,t)}_validateWallRepair(e,t){return this._editorRuntimeOrThrow()._validateWallRepair(e,t)}_applyWallRepair(e,t){return this._editorRuntimeOrThrow()._applyWallRepair(e,t)}_applyWallFaceBatch(){return this._editorRuntimeOrThrow()._applyWallFaceBatch()}_commitRoom(){return this._editorRuntimeOrThrow()._commitRoom()}_cancelPath(){return this._editorRuntimeOrThrow()._cancelPath()}_resumeLastDraft(){return this._editorRuntimeOrThrow()._resumeLastDraft()}_roomDialogCancel(){return this._editorRuntimeOrThrow()._roomDialogCancel()}get _freeAreas(){const e=new Set;for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area&&e.add(i.area);for(const t of this._wallFaceBatch?.decisions||[])t.create&&t.area&&e.add(t.area);return Object.values(this.hass?.areas||{}).filter(t=>!e.has(t.area_id)).sort((e,t)=>(e.name||"").localeCompare(t.name||""))}_openDeviceInbox(){return this._editorRuntimeOrThrow()._openDeviceInbox()}_closeMarkerDialog(){return this._editorRuntimeOrThrow()._closeMarkerDialog()}_deviceInboxCandidates(e){return this._editorRuntimeOrThrow()._deviceInboxCandidates(e)}_deviceInboxRows(){return this._editorRuntimeOrThrow()._deviceInboxRows()}_deviceForInboxRow(e){return this._editorRuntimeOrThrow()._deviceForInboxRow(e)}_openInboxMarker(e,t=!1){return this._editorRuntimeOrThrow()._openInboxMarker(e,t)}async _setInboxHidden(e,t){return this._editorRuntimeOrThrow()._setInboxHidden(e,t)}_findInboxDevice(e){return this._editorRuntimeOrThrow()._findInboxDevice(e)}_deviceInboxTabKey(e){return this._editorRuntimeOrThrow()._deviceInboxTabKey(e)}_openMarkerDialog(e){if(this._editorRuntime)return this._editorRuntime._openMarkerDialog(e);this._ensureEditorRuntime().then(t=>{t&&this._openMarkerDialog(e)})}_runCandidates(){return this._editorRuntimeOrThrow()._runCandidates()}_bindingCandidates(){return this._editorRuntimeOrThrow()._bindingCandidates()}_physicalBodiesR(e=this._spaceModel()){if(!e)return[];const t=`${e.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===t)return this._physicalBodiesCache.all;const i=oa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e));return this._physicalBodiesCache={key:t,...i},i.all}_rawPhysicalBodiesR(e=this._spaceModel()){if(!e)return[];this._physicalBodiesR(e);const t=this._physicalBodiesCache;return t?[...t.drafts,...t.partitions,...t.columns]:[]}_cleanFloor(e,t,i=this._spaceModel()){if(!i)return{floor:t,geom:null,path:"",area:da([[[...t,t[0]]]])};const n=e.id||`#${i.rooms.indexOf(e)}`,r=`${i.id}|${this._cfgEpoch}|${n}`;if(!this._resize?.preview){const e=_g(this._cleanFloorCache,r);if(e.hit)return e.value}const o=t.map(e=>e[0]),s=t.map(e=>e[1]),a=[Math.min(...o),Math.min(...s),Math.max(...o),Math.max(...s)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=a[0]&&Math.min(...t)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?ha(t,l):null,h={floor:t,geom:c,path:c?Qs(c):"",area:da(c||[[[...t,t[0]]]])};return this._resize?.preview||gg(this._cleanFloorCache,r,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const n=this._fullRegistryHass,r=this._bindingStatus(e),o="active"===r.kind?r.enabledEntityIds:r.allEntityIds;if("device"===t){const e=n.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":td(n,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=n.entities?.[i],t=this.hass.states?.[i],r=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":td(n,r,"",[i],this._iconRules)}return""}_allRoomsFlat(){const e=[];for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area?e.push({value:t.id+"#"+i.area,label:(t.title||t.id)+" · "+i.name}):i.id&&e.push({value:t.id+"#@"+i.id,label:(t.title||t.id)+" · "+i.name+" · "+this._t("marker.subarea")});return e}_errText(e){if(!e)return this._t("err.unknown");if("string"==typeof e)return e;if("invalid_passage_fields"===e.code){const t=String(e.message||e.error||"");let i="",n=[];try{const e=JSON.parse(t);i=String(e.space??""),n=Array.isArray(e.fields)?e.fields.map(String):[]}catch{const e=t.match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);e&&(i=e[1],n=e[3].split(",").filter(Boolean))}if(i||n.length){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===i),t={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},r=n.map(e=>t[e]?this._t(t[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||i,fields:r})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"");let i=NaN;try{i=Number(JSON.parse(t).margin_cm)}catch{const e=t.match(/margin_cm=([^;}"]*)/);i=e?Number(e[1]):NaN}if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:Et(i,this._imperial)})}if(null!=e.code){const t=`backup.error.${e.code}`,i=this._t(t);return e.message&&console.warn("[houseplan] backend error",e.code,e.message),i!==t?i:this._t("err.code",{code:e.code})}if(e.message)return e.message;if(e.error)return e.error;try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){return this._editorRuntimeOrThrow()._backupErrorText(e)}async _pickMarkerFiles(e){return this._editorRuntimeOrThrow()._pickMarkerFiles(e)}_removeMarkerPdf(e){return this._editorRuntimeOrThrow()._removeMarkerPdf(e)}_markerLightFields(e){return this._editorRuntimeOrThrow()._markerLightFields(e)}_markerTapActionFields(e){return this._editorRuntimeOrThrow()._markerTapActionFields(e)}_markerToggleEntityFields(e){return this._editorRuntimeOrThrow()._markerToggleEntityFields(e)}async _saveMarker(){return this._editorRuntimeOrThrow()._saveMarker()}async _deleteMarker(){return this._editorRuntimeOrThrow()._deleteMarker()}_normPos(e,t,i){return{s:e,x:t/_f,y:i/_f}}_spaceDialogUsesOnboardingRuntime(e){return"create"===e&&!this._editorRuntime&&(!!this._onboardingRuntime||0===(this._serverCfg?.spaces.length||0)||this._importTotal>0||this._importQueue.length>0)}_spaceRuntimeOrThrow(){if(!this._onboardingRuntime)throw new Error("Houseplan onboarding runtime is not loaded");return this._onboardingRuntime}_openSpaceDialog(e,t){return this._spaceDialogUsesOnboardingRuntime(e)?this._onboardingRuntime?void this._onboardingRuntime._openSpaceDialog(e,t):void this._ensureOnboardingRuntime().then(i=>{i&&this._spaceRuntimeOrThrow()._openSpaceDialog(e,t)}):this._editorRuntime?this._editorRuntime._openSpaceDialog(e,t):void this._ensureEditorRuntime().then(i=>{i&&this._openSpaceDialog(e,t)})}async _pickPlanFile(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._pickPlanFile(e):this._editorRuntimeOrThrow()._pickPlanFile(e)}_useServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._useServerPlan(e):this._editorRuntimeOrThrow()._useServerPlan(e)}async _readPlanAspect(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._readPlanAspect(e):this._editorRuntimeOrThrow()._readPlanAspect(e)}async _deleteServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._deleteServerPlan(e):this._editorRuntimeOrThrow()._deleteServerPlan(e)}_renderServerPlans(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime(e.mode)?this._onboardingRuntime._renderServerPlans(e):this._editorRuntimeOrThrow()._renderServerPlans(e)}async _saveSpaceDialog(){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._saveSpaceDialog():this._editorRuntimeOrThrow()._saveSpaceDialog()}async _deleteSpace(){return this._editorRuntimeOrThrow()._deleteSpace()}async _saveConfigNow(){return this._editorRuntimeOrThrow()._saveConfigNow()}_startImport(){return this._onboardingRuntime?this._onboardingRuntime._startImport():this._editorRuntimeOrThrow()._startImport()}_openNextImport(){return this._onboardingRuntime?this._onboardingRuntime._openNextImport():this._editorRuntimeOrThrow()._openNextImport()}_skipImport(){return this._onboardingRuntime?this._onboardingRuntime._skipImport():this._editorRuntimeOrThrow()._skipImport()}_renderImportDialog(){return this._onboardingRuntime?this._onboardingRuntime._renderImportDialog():this._editorRuntimeOrThrow()._renderImportDialog()}_sunGlobal(){const e=this._settingsDialog;return e?{...this._settings,north_deg:e.northDeg??void 0,bg_mode:e.bgMode,sun_rays:e.sunRays}:this._settings}_sunSpace(){const e=this._spaceDialog,t=this._curSpaceCfg?.settings||{};return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,north_deg:e.northDeg??void 0,bg_mode:e.bgMode??void 0,sun_rays:e.sunRays??void 0}:t}_effNorth(){return Zn(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Jn(this._sunGlobal(),this._sunSpace())}_dayCycleState(e=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:Bn(this._renderPlanHass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?qn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Qn(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?er(this._renderPlanHass):null}_renderSunRays(e){const t=j``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),t;const n=this._effNorth(),r=null!==n?er(this._renderPlanHass):null;if(!r||r.elevation<=0)return this._sunFadeReset(),t;if(o=r.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return t;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const s=this._zeroWalls(),a=s.barriers.map(e=>e.join(",")).join(";"),l=`${e.id}|${r.azimuth}|${r.elevation}|${n}|${this._cfgEpoch}|${s.style}|${a}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const t=e.rooms.map(e=>({id:e.id||"",poly:Ht(e)})).filter(e=>!!e.id&&!!e.poly),i=this._openingsR.filter(e=>"window"===e.type&&"partition"!==e.host?.kind).map(e=>({id:e.id,x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=this._spaceWalls,a=this._openCuts(),c=this._openingWallIndexFor(e,a).value,h={},d={},u=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of t){const t=this._innerRoomContour(e,i.id,a,u);t&&(h[i.id]=t)}for(const e of i){const t=qs(c,{x:e.x,y:e.y,angle:e.angle,length:e.length});t.cm>0&&(d[e.id]=po(t.cm,this._cellCm,this._gridPitch))}}let p=Yn(t,i,r.azimuth,r.elevation,n,o.length?h:void 0,o.length?d:void 0);const m=[...this._physicalBodiesR(e),...s.barriers.map(e=>[[e[0],e[1]],[e[2],e[3]]])];m.length&&(p=p.map(e=>{const t=function(e,t,i){return i>0?e.map(e=>function(e){const t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(t.length<=2)return t;const i=(e,t,i)=>(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0]),n=[];for(const e of t){for(;n.length>=2&&i(n[n.length-2],n[n.length-1],e)<=0;)n.pop();n.push(e)}const r=[];for(let e=t.length-1;e>=0;e--){const n=t[e];for(;r.length>=2&&i(r[r.length-2],r[r.length-1],n)<=0;)r.pop();r.push(n)}return n.slice(0,-1).concat(r.slice(0,-1))}([...e,...e.map(e=>[e[0]+t[0]*i,e[1]+t[1]*i])])).filter(e=>e.length>=3):e}(m,e.dir,e.len),i=e.polys.map(e=>ha(e,t));return{...e,paths:i.map(Qs).filter(Boolean),polys:i.flatMap(pa)}}).filter(e=>e.paths?.length||e.polys.length)),this._sunRaysCache={key:l,rays:p,rims:p.map(e=>function(e,t=1e-4){const[i,n]=e.dir,r=-n,o=i,s=[];for(const a of[e.a,e.b]){const l=[];for(const s of e.polys)for(let e=0;et)continue;if(Math.abs((h[0]-a[0])*r+(h[1]-a[1])*o)>t)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*n,u=(h[0]-a[0])*i+(h[1]-a[1])*n;Math.abs(u-d)<=t||l.push(de[0]-t[0]);const c=[];for(const e of l){const i=c[c.length-1];i&&e[0]<=i[1]+t?i[1]=Math.max(i[1],e[1]):c.push([e[0],e[1]])}for(const[e,t]of c)s.push([[a[0]+i*e,a[1]+n*e],[a[0]+i*t,a[1]+n*t]])}return s}(e))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return t;const d=(u=function(e){const t=Math.min(90,Math.max(-90,Number(e)||0));let i=Wn[Wn.length-1][1];for(let e=1;e + `:j``}_roomHoverPaths(e){const t=this._hoverRoom;if("view"!==this._mode||!t||t.space!==e.id)return null;const i=e.rooms.find(e=>e===t.room||!!e.id&&e.id===t.room.id);if(!i)return null;const n=Ht(i);if(!n)return null;const r=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:Ht(e)})).filter(e=>!!e.poly),o=ci(n,r.map(e=>e.poly)),s=this._openCuts(),a=.02*this._gridPitch,l=s.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return n.some((e,i)=>Mn(t,[e[0],e[1],...n[(i+1)%n.length]])<=4*a)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,s,h)||n,u=[{axis:n,face:d}];for(const t of o){const i=r.find(e=>e.poly===t)?.room;let n=t;if(c.length&&i?.id){const r=hs(e.rooms,i.id,c,s,this._wallKeyPitch,this._cellCm,this._gridPitch,yf);r&&(n=Ns(r.poly,r.offsets)||t)}u.push({axis:t,face:n})}const p=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,n=Math.sin(t)*e.length/2;return[e.x-i,e.y-n,e.x+i,e.y+n]}),m=l.concat(p),_=u.map(({axis:e,face:t})=>{const i=m.map(i=>((e,t,i)=>{const n=e[2]-e[0],r=e[3]-e[1],o=Math.hypot(n,r);if(o.05)&&Mn([c,h],[i[0],i[1],n[0],n[1]])<=4*a){d=!0;break}}if(!d)return null;let u=null;for(let e=0;e.05)continue;const p=Mn([c,h],[t[0],t[1],n[0],n[1]]);(!u||p!!e);return i.length?mn(t,i,a).map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" "):`M ${t.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!_)return null;const g=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,e).path||g(d),...o.map(g)].join(" "),outlineD:_}}_renderRoomHoverFill(e){return j` + + `}_renderRoomHoverOutline(e){return j` + + + `}_renderWallThickUi(){return this._editorRuntime?this._editorRuntimeOrThrow()._renderWallThickUi():j``}_renderWallThickDialog(){return this._editorRuntimeOrThrow()._renderWallThickDialog()}_openingAt(e){return this._editorRuntimeOrThrow()._openingAt(e)}_resolveOpeningPlacement(e){return this._editorRuntimeOrThrow()._resolveOpeningPlacement(e)}_activateOpeningPlacement(e){return this._editorRuntimeOrThrow()._activateOpeningPlacement(e)}_clearOpeningPlacement(e){return this._editorRuntime?this._editorRuntimeOrThrow()._clearOpeningPlacement(e):(this._openingHoverCandidate=null,this._openingJambBlockCm=null,void(e&&(this._openingPreset=null,this._openingRebindId=null)))}_openingClick(e){return this._editorRuntimeOrThrow()._openingClick(e)}_editOpening(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._editOpening(e)}_opPointerDown(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerDown(e,t)}_opPointerMove(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerMove(e,t)}_opRuler(e,t){return this._editorRuntimeOrThrow()._opRuler(e,t)}_opPointerUp(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerUp(e,t)}_opClick(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opClick(e,t)}_saveOpening(){return this._editorRuntimeOrThrow()._saveOpening()}_deleteOpening(){return this._editorRuntimeOrThrow()._deleteOpening()}_contactCandidates(){return this._editorRuntimeOrThrow()._contactCandidates()}_lockCandidates(){return this._editorRuntimeOrThrow()._lockCandidates()}_toggleOpeningEntityPicker(e){return this._editorRuntimeOrThrow()._toggleOpeningEntityPicker(e)}_filterOpeningEntities(e,t){return this._editorRuntimeOrThrow()._filterOpeningEntities(e,t)}_selectOpeningEntity(e,t){return this._editorRuntimeOrThrow()._selectOpeningEntity(e,t)}_mergeClick(e){return this._editorRuntimeOrThrow()._mergeClick(e)}_commitMerge(){return this._editorRuntimeOrThrow()._commitMerge()}_splitClick(e){return this._editorRuntimeOrThrow()._splitClick(e)}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])&&(null!=this._closingWallCm||!this._activeDraftId)}_markupMove(e){this._markup&&this._editorRuntime&&this._editorRuntimeOrThrow()._queuePointerMove("markup-hover",()=>this._editorRuntime._markupMove(e))}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&nf(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const n=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=n,n}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){return this._editorRuntimeOrThrow()._saveRoom()}_decideWallFace(e){return this._editorRuntimeOrThrow()._decideWallFace(e)}_wallSourceCmAt(e,t,i){return this._editorRuntimeOrThrow()._wallSourceCmAt(e,t,i)}_activePathWithRepair(e,t){return this._editorRuntimeOrThrow()._activePathWithRepair(e,t)}_validateWallRepair(e,t){return this._editorRuntimeOrThrow()._validateWallRepair(e,t)}_applyWallRepair(e,t){return this._editorRuntimeOrThrow()._applyWallRepair(e,t)}_applyWallFaceBatch(){return this._editorRuntimeOrThrow()._applyWallFaceBatch()}_commitRoom(){return this._editorRuntimeOrThrow()._commitRoom()}_cancelPath(){return this._editorRuntimeOrThrow()._cancelPath()}_resumeLastDraft(){return this._editorRuntimeOrThrow()._resumeLastDraft()}_roomDialogCancel(){return this._editorRuntimeOrThrow()._roomDialogCancel()}get _freeAreas(){const e=new Set;for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area&&e.add(i.area);for(const t of this._wallFaceBatch?.decisions||[])t.create&&t.area&&e.add(t.area);return Object.values(this.hass?.areas||{}).filter(t=>!e.has(t.area_id)).sort((e,t)=>(e.name||"").localeCompare(t.name||""))}_openDeviceInbox(){return this._editorRuntimeOrThrow()._openDeviceInbox()}_closeMarkerDialog(){return this._editorRuntimeOrThrow()._closeMarkerDialog()}_deviceInboxCandidates(e){return this._editorRuntimeOrThrow()._deviceInboxCandidates(e)}_deviceInboxRows(){return this._editorRuntimeOrThrow()._deviceInboxRows()}_deviceForInboxRow(e){return this._editorRuntimeOrThrow()._deviceForInboxRow(e)}_openInboxMarker(e,t=!1){return this._editorRuntimeOrThrow()._openInboxMarker(e,t)}async _setInboxHidden(e,t){return this._editorRuntimeOrThrow()._setInboxHidden(e,t)}_findInboxDevice(e){return this._editorRuntimeOrThrow()._findInboxDevice(e)}_deviceInboxTabKey(e){return this._editorRuntimeOrThrow()._deviceInboxTabKey(e)}_openMarkerDialog(e){if(this._editorRuntime)return this._editorRuntime._openMarkerDialog(e);this._ensureEditorRuntime().then(t=>{t&&this._openMarkerDialog(e)})}_runCandidates(){return this._editorRuntimeOrThrow()._runCandidates()}_bindingCandidates(){return this._editorRuntimeOrThrow()._bindingCandidates()}_physicalBodiesR(e=this._spaceModel()){if(!e)return[];const t=`${e.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===t)return this._physicalBodiesCache.all;const i=sa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e));return this._physicalBodiesCache={key:t,...i},i.all}_rawPhysicalBodiesR(e=this._spaceModel()){if(!e)return[];this._physicalBodiesR(e);const t=this._physicalBodiesCache;return t?[...t.drafts,...t.partitions,...t.columns]:[]}_cleanFloor(e,t,i=this._spaceModel()){if(!i)return{floor:t,geom:null,path:"",area:ua([[[...t,t[0]]]])};const n=e.id||`#${i.rooms.indexOf(e)}`,r=`${i.id}|${this._cfgEpoch}|${n}`;if(!this._resize?.preview){const e=yg(this._cleanFloorCache,r);if(e.hit)return e.value}const o=t.map(e=>e[0]),s=t.map(e=>e[1]),a=[Math.min(...o),Math.min(...s),Math.max(...o),Math.max(...s)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=a[0]&&Math.min(...t)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?da(t,l):null,h={floor:t,geom:c,path:c?ea(c):"",area:ua(c||[[[...t,t[0]]]])};return this._resize?.preview||bg(this._cleanFloorCache,r,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const n=this._fullRegistryHass,r=this._bindingStatus(e),o="active"===r.kind?r.enabledEntityIds:r.allEntityIds;if("device"===t){const e=n.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":nd(n,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=n.entities?.[i],t=this.hass.states?.[i],r=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":nd(n,r,"",[i],this._iconRules)}return""}_allRoomsFlat(){const e=[];for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area?e.push({value:t.id+"#"+i.area,label:(t.title||t.id)+" · "+i.name}):i.id&&e.push({value:t.id+"#@"+i.id,label:(t.title||t.id)+" · "+i.name+" · "+this._t("marker.subarea")});return e}_errText(e){if(!e)return this._t("err.unknown");if("string"==typeof e)return e;if("invalid_passage_fields"===e.code){const t=String(e.message||e.error||"");let i="",n=[];try{const e=JSON.parse(t);i=String(e.space??""),n=Array.isArray(e.fields)?e.fields.map(String):[]}catch{const e=t.match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);e&&(i=e[1],n=e[3].split(",").filter(Boolean))}if(i||n.length){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===i),t={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},r=n.map(e=>t[e]?this._t(t[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||i,fields:r})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"");let i=NaN;try{i=Number(JSON.parse(t).margin_cm)}catch{const e=t.match(/margin_cm=([^;}"]*)/);i=e?Number(e[1]):NaN}if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:Et(i,this._imperial)})}if(null!=e.code){const t=`backup.error.${e.code}`,i=this._t(t);return e.message&&console.warn("[houseplan] backend error",e.code,e.message),i!==t?i:this._t("err.code",{code:e.code})}if(e.message)return e.message;if(e.error)return e.error;try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){return this._editorRuntimeOrThrow()._backupErrorText(e)}async _pickMarkerFiles(e){return this._editorRuntimeOrThrow()._pickMarkerFiles(e)}_removeMarkerPdf(e){return this._editorRuntimeOrThrow()._removeMarkerPdf(e)}_markerLightFields(e){return this._editorRuntimeOrThrow()._markerLightFields(e)}_markerTapActionFields(e){return this._editorRuntimeOrThrow()._markerTapActionFields(e)}_markerToggleEntityFields(e){return this._editorRuntimeOrThrow()._markerToggleEntityFields(e)}async _saveMarker(){return this._editorRuntimeOrThrow()._saveMarker()}async _deleteMarker(){return this._editorRuntimeOrThrow()._deleteMarker()}_normPos(e,t,i){return{s:e,x:t/yf,y:i/yf}}_spaceDialogUsesOnboardingRuntime(e){return"create"===e&&!this._editorRuntime&&(!!this._onboardingRuntime||0===(this._serverCfg?.spaces.length||0)||this._importTotal>0||this._importQueue.length>0)}_spaceRuntimeOrThrow(){if(!this._onboardingRuntime)throw new Error("Houseplan onboarding runtime is not loaded");return this._onboardingRuntime}_openSpaceDialog(e,t){return this._spaceDialogUsesOnboardingRuntime(e)?this._onboardingRuntime?void this._onboardingRuntime._openSpaceDialog(e,t):void this._ensureOnboardingRuntime().then(i=>{i&&this._spaceRuntimeOrThrow()._openSpaceDialog(e,t)}):this._editorRuntime?this._editorRuntime._openSpaceDialog(e,t):void this._ensureEditorRuntime().then(i=>{i&&this._openSpaceDialog(e,t)})}async _pickPlanFile(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._pickPlanFile(e):this._editorRuntimeOrThrow()._pickPlanFile(e)}_useServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._useServerPlan(e):this._editorRuntimeOrThrow()._useServerPlan(e)}async _readPlanAspect(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._readPlanAspect(e):this._editorRuntimeOrThrow()._readPlanAspect(e)}async _deleteServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._deleteServerPlan(e):this._editorRuntimeOrThrow()._deleteServerPlan(e)}_renderServerPlans(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime(e.mode)?this._onboardingRuntime._renderServerPlans(e):this._editorRuntimeOrThrow()._renderServerPlans(e)}async _saveSpaceDialog(){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._saveSpaceDialog():this._editorRuntimeOrThrow()._saveSpaceDialog()}async _deleteSpace(){return this._editorRuntimeOrThrow()._deleteSpace()}async _saveConfigNow(){return this._editorRuntimeOrThrow()._saveConfigNow()}_startImport(){return this._onboardingRuntime?this._onboardingRuntime._startImport():this._editorRuntimeOrThrow()._startImport()}_openNextImport(){return this._onboardingRuntime?this._onboardingRuntime._openNextImport():this._editorRuntimeOrThrow()._openNextImport()}_skipImport(){return this._onboardingRuntime?this._onboardingRuntime._skipImport():this._editorRuntimeOrThrow()._skipImport()}_renderImportDialog(){return this._onboardingRuntime?this._onboardingRuntime._renderImportDialog():this._editorRuntimeOrThrow()._renderImportDialog()}_sunGlobal(){const e=this._settingsDialog;return e?{...this._settings,north_deg:e.northDeg??void 0,bg_mode:e.bgMode,sun_rays:e.sunRays}:this._settings}_sunSpace(){const e=this._spaceDialog,t=this._curSpaceCfg?.settings||{};return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,north_deg:e.northDeg??void 0,bg_mode:e.bgMode??void 0,sun_rays:e.sunRays??void 0}:t}_effNorth(){return Zn(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Jn(this._sunGlobal(),this._sunSpace())}_dayCycleState(e=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:Bn(this._renderPlanHass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?qn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Qn(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?er(this._renderPlanHass):null}_renderSunRays(e){const t=j``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),t;const n=this._effNorth(),r=null!==n?er(this._renderPlanHass):null;if(!r||r.elevation<=0)return this._sunFadeReset(),t;if(o=r.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return t;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const s=this._zeroWalls(),a=s.barriers.map(e=>e.join(",")).join(";"),l=`${e.id}|${r.azimuth}|${r.elevation}|${n}|${this._cfgEpoch}|${s.style}|${a}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const t=e.rooms.map(e=>({id:e.id||"",poly:Ht(e)})).filter(e=>!!e.id&&!!e.poly),i=this._openingsR.filter(e=>"window"===e.type&&"partition"!==e.host?.kind).map(e=>({id:e.id,x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=this._spaceWalls,a=this._openCuts(),c=this._openingWallIndexFor(e,a).value,h={},d={},u=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of t){const t=this._innerRoomContour(e,i.id,a,u);t&&(h[i.id]=t)}for(const e of i){const t=Ws(c,{x:e.x,y:e.y,angle:e.angle,length:e.length});t.cm>0&&(d[e.id]=po(t.cm,this._cellCm,this._gridPitch))}}let p=Yn(t,i,r.azimuth,r.elevation,n,o.length?h:void 0,o.length?d:void 0);const m=[...this._physicalBodiesR(e),...s.barriers.map(e=>[[e[0],e[1]],[e[2],e[3]]])];m.length&&(p=p.map(e=>{const t=function(e,t,i){return i>0?e.map(e=>function(e){const t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(t.length<=2)return t;const i=(e,t,i)=>(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0]),n=[];for(const e of t){for(;n.length>=2&&i(n[n.length-2],n[n.length-1],e)<=0;)n.pop();n.push(e)}const r=[];for(let e=t.length-1;e>=0;e--){const n=t[e];for(;r.length>=2&&i(r[r.length-2],r[r.length-1],n)<=0;)r.pop();r.push(n)}return n.slice(0,-1).concat(r.slice(0,-1))}([...e,...e.map(e=>[e[0]+t[0]*i,e[1]+t[1]*i])])).filter(e=>e.length>=3):e}(m,e.dir,e.len),i=e.polys.map(e=>da(e,t));return{...e,paths:i.map(ea).filter(Boolean),polys:i.flatMap(ma)}}).filter(e=>e.paths?.length||e.polys.length)),this._sunRaysCache={key:l,rays:p,rims:p.map(e=>function(e,t=1e-4){const[i,n]=e.dir,r=-n,o=i,s=[];for(const a of[e.a,e.b]){const l=[];for(const s of e.polys)for(let e=0;et)continue;if(Math.abs((h[0]-a[0])*r+(h[1]-a[1])*o)>t)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*n,u=(h[0]-a[0])*i+(h[1]-a[1])*n;Math.abs(u-d)<=t||l.push(de[0]-t[0]);const c=[];for(const e of l){const i=c[c.length-1];i&&e[0]<=i[1]+t?i[1]=Math.max(i[1],e[1]):c.push([e[0],e[1]])}for(const[e,t]of c)s.push([[a[0]+i*e,a[1]+n*e],[a[0]+i*t,a[1]+n*t]])}return s}(e))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return t;const d=(u=function(e){const t=Math.min(90,Math.max(-90,Number(e)||0));let i=Wn[Wn.length-1][1];for(let e=1;e ${c.map((e,t)=>{const i=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2,r=i+e.normal[0]*e.depth,o=n+e.normal[1]*e.depth;return j` ${p.map(([e,t])=>j`${this._t("gs.north_letter")} ${null===e?"—":e+"°"} - `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,n=t?t.bgColor||"":Gi(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||n}_stageBgHex(){const e=this._stageEl;if(e){const t=getComputedStyle(e).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(t)return"#"+t.slice(1,4).map(e=>(+e).toString(16).padStart(2,"0")).join("")}return"#111111"}_preflightDiagnostics(e,t){return this._editorRuntimeOrThrow()._preflightDiagnostics(e,t)}_reportPreflightFailure(e,t){return this._editorRuntimeOrThrow()._reportPreflightFailure(e,t)}_preflightVersionsDiffer(){return this._editorRuntimeOrThrow()._preflightVersionsDiffer()}async _copyPreflightDiagnostics(){return this._editorRuntimeOrThrow()._copyPreflightDiagnostics()}_checkOptimizeGeometry(e){return this._editorRuntimeOrThrow()._checkOptimizeGeometryImpl(e)}_checkSpacePhysicalGeometry(e,t,i){return this._editorRuntimeOrThrow()._checkSpacePhysicalGeometryImpl(e,t,i)}_optimizeReferenceContext(e){return this._editorRuntimeOrThrow()._optimizeReferenceContext(e)}_previewAlignDialog(e){return this._editorRuntimeOrThrow()._previewAlignDialog(e)}async _runAlignToGrid(){return this._editorRuntimeOrThrow()._runAlignToGrid()}async _undoPlanOptimization(){return this._editorRuntimeOrThrow()._undoPlanOptimization()}async _runBackupExport(){return this._editorRuntimeOrThrow()._runBackupExport()}async _pickBackupImport(e){return this._editorRuntimeOrThrow()._pickBackupImport(e)}async _setBackupDuplicatePolicy(e){return this._editorRuntimeOrThrow()._setBackupDuplicatePolicy(e)}async _applyBackupImport(){return this._editorRuntimeOrThrow()._applyBackupImport()}_renderBackupExportDialog(){return this._editorRuntimeOrThrow()._renderBackupExportDialog()}_renderBackupImportDialog(){return this._editorRuntimeOrThrow()._renderBackupImportDialog()}_setFillColor(e,t){return this._editorRuntimeOrThrow()._setFillColor(e,t)}async _saveSettingsDialog(){return this._editorRuntimeOrThrow()._saveSettingsDialog()}_boolInput(e,t,i=!1){return this._editorRuntimeOrThrow()._boolInput(e,t,i)}_rangeInput(e,t,i,n,r,o=!1,s){return this._editorRuntimeOrThrow()._rangeInput(e,t,i,n,r,o,s)}_renderColorRow(e,t){return this._editorRuntimeOrThrow()._renderColorRow(e,t)}get _glowRadiusCm(){const e=Number(this._settings.glow_radius_cm);return Number.isFinite(e)&&e>0?e:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const e=this._glowRadiusCm;return this._imperial?String(Math.round(e/30.48*10)/10):String(e/100)}_glowTransition(e,t){return Im(this._glowRuntimeState,this._glowRuntimeHost,e,t)}_forgetGlowSource(e){Em(this._glowRuntimeState,this._glowRuntimeHost,e)}_forgetGlowSpace(e){Nm(this._glowRuntimeState,this._glowRuntimeHost,e)}_warnGlowGeometryFallback(e,t,i,n){Bm(this._glowRuntimeState,e,t,i,n)}_lightBarriers(e,t){const i=zm({rawSpaceConfig:this._curSpaceCfg,space:e,openings:this._openingsR,cellCm:this._cellCm,gridPitch:this._gridPitch,openingAmount:e=>this._openingAmt(e)}),n=`${e.id}|${i.fingerprint}`,r=_g(this._lightBarrierPool,n);if(r.hit)return this._lightBarrierCache=r.value,r.value.value;const o=Am({space:e,revision:i,walls:this._spaceWalls,zeroWalls:this._zeroWalls(),wallKeyPitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:_f,sharedWallGeometry:this._wallUnionGeometry(),physicalBodies:(t,i)=>(this._lightPhysicalBodiesCache?.key!==i&&(this._lightPhysicalBodiesCache={key:i,all:oa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,t).all}),this._lightPhysicalBodiesCache.all)}),s={key:n,value:o};return gg(this._lightBarrierPool,n,s,8),this._lightBarrierCache=s,o}_renderGlowLayer(e,t){const i=this._fillColors,n=this._glowRadiusCm/this._cellCm*this._gridPitch,r=e.rooms.map(e=>({r:e,poly:Ht(e)})).filter(e=>!!e.poly),o=r.filter(({r:e})=>bn(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const s=this._lightBarriers(e,r),a=Dm({hass:this._renderPlanHass,devices:this._renderDevices,virtualLights:this._virtualLights,spaceId:e.id,defaultColor:i.glow_light.c,paletteAlpha:i.glow_light.a,defaultRadiusUnits:n,cellCm:this._cellCm,gridPitch:this._gridPitch,position:e=>this._pos(e)}),l=[],c=new Set;for(const t of a){const{key:i,pos:n}=t;if(c.add(i),Fm(n,s)){this._forgetGlowSource(i);continue}const o=this._glowTransition(i,!!t.appearance);if(!o)continue;t.appearance&&this._glowLastAppearance.set(i,t.appearance);const a=this._glowLastAppearance.get(i);if(!a)continue;let h=null;const d=`${e.id}|${s.fingerprint}|${n.x.toFixed(4)},${n.y.toFixed(4)}|${t.radius.toFixed(4)}`,u=Rm(this._glowRuntimeState,d);u.hit?h=u.value:(h=Pm({spaceId:e.id,source:n,radius:t.radius,scene:s,polygons:r.map(({r:e,poly:t})=>({room:e,poly:t})),onBoundsFailure:(t,i)=>this._warnGlowGeometryFallback(e.id,s.fingerprint,t,i)}),Tm(this._glowRuntimeState,d,h)),l.push({key:i,sourceEid:t.sourceEid,domId:o.domId,entering:o.entering,leaving:o.leaving,pos:n,c:a.c,alpha:a.alpha,geometry:h,r:t.radius})}if(Hm(this._glowRuntimeState,this._glowRuntimeHost,e.id,c),!l.length)return j``;const h=this._spaceWalls,d=o.length===r.length?[]:this._openCuts(),u=this._wallUnionGeometry()?.roomGeom,p=o.length===r.length?null:o.map(({r:t,poly:i})=>{const n=h.length&&t.id&&this._innerRoomContour(e,t.id,d,u)||i,o=this._cleanFloor(t,n,e).path,s=ci(n,r.filter(e=>e.r!==t).map(e=>e.poly)),a=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||a(n),...s.map(a)].join(" ")}),m=this._viewOr(this._baseVb()),_=this._stageEl?.clientWidth&&m.w?this._stageEl.clientWidth/m.w:1,g=!this._pinchStart&&!this._panStart&&!this._cameraTransition.active,f=qm(this._glowRuntimeState,_,g);return Wm({spots:l,enabledClip:p,feather:f.feather,featherEnabled:f.enabled,screenBlend:this._glowScreenBlend})}_renderAlignDialog(){return this._editorRuntimeOrThrow()._renderAlignDialog()}_renderSettingsDialog(){return this._editorRuntimeOrThrow()._renderSettingsDialog()}_renderSupportDialog(){return this._editorRuntimeOrThrow()._renderSupportDialog()}_rulesSet(e){return this._editorRuntimeOrThrow()._rulesSet(e)}async _saveRules(){return this._editorRuntimeOrThrow()._saveRules()}_renderRulesDialog(){return this._editorRuntimeOrThrow()._renderRulesDialog()}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(uf,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>{const n=Math.round(100*e[t]);return W` + `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,n=t?t.bgColor||"":Gi(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||n}_stageBgHex(){const e=this._stageEl;if(e){const t=getComputedStyle(e).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(t)return"#"+t.slice(1,4).map(e=>(+e).toString(16).padStart(2,"0")).join("")}return"#111111"}_preflightDiagnostics(e,t){return this._editorRuntimeOrThrow()._preflightDiagnostics(e,t)}_reportPreflightFailure(e,t){return this._editorRuntimeOrThrow()._reportPreflightFailure(e,t)}_preflightVersionsDiffer(){return this._editorRuntimeOrThrow()._preflightVersionsDiffer()}async _copyPreflightDiagnostics(){return this._editorRuntimeOrThrow()._copyPreflightDiagnostics()}_checkOptimizeGeometry(e){return this._editorRuntimeOrThrow()._checkOptimizeGeometryImpl(e)}_checkSpacePhysicalGeometry(e,t,i){return this._editorRuntimeOrThrow()._checkSpacePhysicalGeometryImpl(e,t,i)}_optimizeReferenceContext(e){return this._editorRuntimeOrThrow()._optimizeReferenceContext(e)}_previewAlignDialog(e){return this._editorRuntimeOrThrow()._previewAlignDialog(e)}async _runAlignToGrid(){return this._editorRuntimeOrThrow()._runAlignToGrid()}async _undoPlanOptimization(){return this._editorRuntimeOrThrow()._undoPlanOptimization()}async _runBackupExport(){return this._editorRuntimeOrThrow()._runBackupExport()}async _pickBackupImport(e){return this._editorRuntimeOrThrow()._pickBackupImport(e)}async _setBackupDuplicatePolicy(e){return this._editorRuntimeOrThrow()._setBackupDuplicatePolicy(e)}async _applyBackupImport(){return this._editorRuntimeOrThrow()._applyBackupImport()}_renderBackupExportDialog(){return this._editorRuntimeOrThrow()._renderBackupExportDialog()}_renderBackupImportDialog(){return this._editorRuntimeOrThrow()._renderBackupImportDialog()}_setFillColor(e,t){return this._editorRuntimeOrThrow()._setFillColor(e,t)}async _saveSettingsDialog(){return this._editorRuntimeOrThrow()._saveSettingsDialog()}_boolInput(e,t,i=!1){return this._editorRuntimeOrThrow()._boolInput(e,t,i)}_rangeInput(e,t,i,n,r,o=!1,s){return this._editorRuntimeOrThrow()._rangeInput(e,t,i,n,r,o,s)}_renderColorRow(e,t){return this._editorRuntimeOrThrow()._renderColorRow(e,t)}get _glowRadiusCm(){const e=Number(this._settings.glow_radius_cm);return Number.isFinite(e)&&e>0?e:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const e=this._glowRadiusCm;return this._imperial?String(Math.round(e/30.48*10)/10):String(e/100)}_glowTransition(e,t){return Nm(this._glowRuntimeState,this._glowRuntimeHost,e,t)}_forgetGlowSource(e){Hm(this._glowRuntimeState,this._glowRuntimeHost,e)}_forgetGlowSpace(e){Lm(this._glowRuntimeState,this._glowRuntimeHost,e)}_warnGlowGeometryFallback(e,t,i,n){Wm(this._glowRuntimeState,e,t,i,n)}_lightBarriers(e,t){const i=Pm({rawSpaceConfig:this._curSpaceCfg,space:e,openings:this._openingsR,cellCm:this._cellCm,gridPitch:this._gridPitch,openingAmount:e=>this._openingAmt(e)}),n=`${e.id}|${i.fingerprint}`,r=yg(this._lightBarrierPool,n);if(r.hit)return this._lightBarrierCache=r.value,r.value.value;const o=Fm({space:e,revision:i,walls:this._spaceWalls,zeroWalls:this._zeroWalls(),wallKeyPitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:yf,sharedWallGeometry:this._wallUnionGeometry(),physicalBodies:(t,i)=>(this._lightPhysicalBodiesCache?.key!==i&&(this._lightPhysicalBodiesCache={key:i,all:sa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,t).all}),this._lightPhysicalBodiesCache.all)}),s={key:n,value:o};return bg(this._lightBarrierPool,n,s,8),this._lightBarrierCache=s,o}_renderGlowLayer(e,t){const i=this._fillColors,n=this._glowRadiusCm/this._cellCm*this._gridPitch,r=e.rooms.map(e=>({r:e,poly:Ht(e)})).filter(e=>!!e.poly),o=r.filter(({r:e})=>bn(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const s=this._lightBarriers(e,r),a=Am({hass:this._renderPlanHass,devices:this._renderDevices,virtualLights:this._virtualLights,spaceId:e.id,defaultColor:i.glow_light.c,paletteAlpha:i.glow_light.a,defaultRadiusUnits:n,cellCm:this._cellCm,gridPitch:this._gridPitch,position:e=>this._pos(e)}),l=[],c=new Set;for(const t of a){const{key:i,pos:n}=t;if(c.add(i),Im(n,s)){this._forgetGlowSource(i);continue}const o=this._glowTransition(i,!!t.appearance);if(!o)continue;t.appearance&&this._glowLastAppearance.set(i,t.appearance);const a=this._glowLastAppearance.get(i);if(!a)continue;let h=null;const d=`${e.id}|${s.fingerprint}|${n.x.toFixed(4)},${n.y.toFixed(4)}|${t.radius.toFixed(4)}`,u=Dm(this._glowRuntimeState,d);u.hit?h=u.value:(h=Om({spaceId:e.id,source:n,radius:t.radius,scene:s,polygons:r.map(({r:e,poly:t})=>({room:e,poly:t})),onBoundsFailure:(t,i)=>this._warnGlowGeometryFallback(e.id,s.fingerprint,t,i)}),zm(this._glowRuntimeState,d,h)),l.push({key:i,sourceEid:t.sourceEid,domId:o.domId,entering:o.entering,leaving:o.leaving,pos:n,c:a.c,alpha:a.alpha,geometry:h,r:t.radius})}if(Bm(this._glowRuntimeState,this._glowRuntimeHost,e.id,c),!l.length)return j``;const h=this._spaceWalls,d=o.length===r.length?[]:this._openCuts(),u=this._wallUnionGeometry()?.roomGeom,p=o.length===r.length?null:o.map(({r:t,poly:i})=>{const n=h.length&&t.id&&this._innerRoomContour(e,t.id,d,u)||i,o=this._cleanFloor(t,n,e).path,s=ci(n,r.filter(e=>e.r!==t).map(e=>e.poly)),a=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||a(n),...s.map(a)].join(" ")}),m=this._viewOr(this._baseVb()),_=this._stageEl?.clientWidth&&m.w?this._stageEl.clientWidth/m.w:1,g=!this._pinchStart&&!this._panStart&&!this._cameraTransition.active,f=jm(this._glowRuntimeState,_,g);return Um({spots:l,enabledClip:p,feather:f.feather,featherEnabled:f.enabled,screenBlend:this._glowScreenBlend})}_renderAlignDialog(){return this._editorRuntimeOrThrow()._renderAlignDialog()}_renderSettingsDialog(){return this._editorRuntimeOrThrow()._renderSettingsDialog()}_renderSupportDialog(){return this._editorRuntimeOrThrow()._renderSupportDialog()}_rulesSet(e){return this._editorRuntimeOrThrow()._rulesSet(e)}async _saveRules(){return this._editorRuntimeOrThrow()._saveRules()}_renderRulesDialog(){return this._editorRuntimeOrThrow()._renderRulesDialog()}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(gf,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>{const n=Math.round(100*e[t]);return W`
{const i=Number(e.target.value);Number.isFinite(i)&&this._saveKioskScale({[t]:i/100})}} aria-label=${i} /> @@ -5154,7 +5157,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef .request=${this._dangerConfirm.request} .token=${this._dangerConfirm.token} @hp-confirm-decision=${this._onDangerConfirmDecision}> - `:V}_renderRoot(e){return W`${e}${this._renderDangerConfirm()}`}render(){const e=this._renderBody();return e===V?e:this._renderRoot(e)}_renderBody(){if(!this._config||!this.hass)return V;const e=this._syncDangerConfirmLocaleGate();if("cold"===e)return du();if("warm"===e)return U;const t=!!this._importDialog||!(!this._spaceDialog||!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)),i="view"!==this._mode||!!(this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._partitionDeleteDialog||this._roomDeleteDialog||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog&&!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)||this._deviceInbox||this._markerDialog||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._backupExportDialog||this._backupImportDialog||this._kioskDialog||this._vacFit||this._vacCalConfirm);t&&!this._onboardingRuntime&&this._ensureOnboardingRuntime(),i&&!this._editorRuntime&&this._ensureEditorRuntime();const n=this._model,r=this.houseplanDiagnostics(),o=this._fixedFloorState(n);if("pending"===o.kind)return W` + `:V}_renderRoot(e){return W`${e}${this._renderDangerConfirm()}`}render(){const e=this._renderBody();return e===V?e:this._renderRoot(e)}_renderBody(){if(!this._config||!this.hass)return V;const e=this._terminalFrame;if(this._terminalFrame=0,1===e)return U;const t=this._syncDangerConfirmLocaleGate();if("cold"===t)return pu();if("warm"===t)return U;const i=!!this._importDialog||!(!this._spaceDialog||!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)),n="view"!==this._mode||!!(this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._partitionDeleteDialog||this._roomDeleteDialog||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog&&!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)||this._deviceInbox||this._markerDialog||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._backupExportDialog||this._backupImportDialog||this._kioskDialog||this._vacFit||this._vacCalConfirm);i&&!this._onboardingRuntime&&this._ensureOnboardingRuntime(),n&&!this._editorRuntime&&this._ensureEditorRuntime();const r=this._model,o=this._renderLife.diagnostics(this.hass,this._markers,e=>this._bindingStatus(e)),s=this._fixedFloorState(r);if("pending"===s.kind)return W`
${this._config.title||this._t("card.title")}
@@ -5162,25 +5165,25 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef

${this._t("fixed_floor.loading")}

- `;if("invalid"===o.kind)return W``;if("invalid"===s.kind)return W` + data-fixed-floor-reason=${s.reason}>
${this._config.title||this._t("card.title")}
-
`;if(!n.length)return W``;if(!r.length)return W` + data-ha-registry-access=${o.registry.access} + data-ha-disabled-bindings=${o.bindings.ha_disabled} + data-ha-unverified-bindings=${o.bindings.unverified}>
${this._config.title||this._t("card.title")}
@@ -5195,16 +5198,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef ${this._spaceDialog&&(this._onboardingRuntime||this._editorRuntime)?this._renderSpaceDialog():V} ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():V} ${this._toast?W``:V} -
`;const s=this._spaceModel();if(!s)return V;const a="valid"===o.kind?[s]:n,l=s.vb,c=this._effectiveProjection();this._renderProjection=c,this._labs.active.length&&function(){const e=Sg,t=e.alpha?e.active.join(","):"";if(!t||t===Cg)return;Cg=t;const i=new Map(bg.map(e=>[e.id,e])),n=e.active.map(e=>`${e} (#${i.get(e).issue})`).join(", ");console.info(`HOUSEPLAN ALPHA: hp_alpha=1; ${n}`)}();const h="iso"===c,d="devices"===this._mode&&this._showAll,u=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||d)),p=this._renderDeviceSnapshot,m=this._spaceDisplayForRender(),_=h?this._isoDecorationLayers(m):null,g=_?this._isoOpeningPanels(_):[],f=this._resolvedRoomFills(s,m),v=this._resolvedGlowBase(s,m,f),y=m.showLqi??this._config.show_signal??!0,b=this._config.icon_size??2.5,w=b>8?2.5:b,k=vp(w),x=this._viewOr(this._baseVb()),$=this._floorView(x),S=this._editing?"":this._stageBg(m),M=this._opMeasureView,C=this._decorMeasure,R=this._bdLive,T=this._editorRuntime?._furnLive()??null,D="view"===this._mode?this._editorChromeMode:this._mode,z=this._roomHoverPaths(s),A=s.bg?this._display(s.bg.href):"",P=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,F=this._modeTransitionVisual,O=this._dayCycleState(),I=F?.viewWeight??("view"===this._mode?1:0),E=this._modeTransition.state?.from.presentedMode,N=!this._markup||!!F&&("view"===F.presentedMode||"devices"===F.presentedMode||"view"===E||"devices"===E),H=F?.stageColor||S,L=F?.sceneBrightness??1;return W` +
`;const a=this._spaceModel();if(!a)return V;const l="valid"===s.kind?[a]:r,c=a.vb,h=this._effectiveProjection();this._renderProjection=h,this._labs.active.length&&function(){const e=Tg,t=e.alpha?e.active.join(","):"";if(!t||t===zg)return;zg=t;const i=new Map(Sg.map(e=>[e.id,e])),n=e.active.map(e=>`${e} (#${i.get(e).issue})`).join(", ");console.info(`HOUSEPLAN ALPHA: hp_alpha=1; ${n}`)}();const d="iso"===h,u="devices"===this._mode&&this._showAll,p=this._renderDevices.filter(e=>e.space===a.id&&(!e.hidden||u)),m=this._renderDeviceSnapshot,_=this._spaceDisplayForRender(),g=d?this._isoDecorationLayers(_):null,f=g?this._isoOpeningPanels(g):[],v=this._resolvedRoomFills(a,_),y=this._resolvedGlowBase(a,_,v),b=_.showLqi??this._config.show_signal??!0,w=this._config.icon_size??2.5,k=w>8?2.5:w,x=bp(k),S=this._viewOr(this._baseVb()),$=this._floorView(S),M=this._editing?"":this._stageBg(_),C=this._opMeasureView,R="view"===this._mode?this._editorChromeMode:this._mode,T=this._roomHoverPaths(a),D=a.bg?this._display(a.bg.href):"",z=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,A=this._modeTransitionVisual,P=this._dayCycleState(),F=A?.viewWeight??("view"===this._mode?1:0),O=this._modeTransition.state?.from.presentedMode,I=!this._markup||!!A&&("view"===A.presentedMode||"devices"===A.presentedMode||"view"===O||"devices"===O),E=A?.stageColor||M,N=A?.sceneBrightness??1;return W` this._notePointer(e)} @pointerdown=${this._touchGestureGuard} @pointermove=${this._touchGestureGuard} @@ -5219,7 +5222,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef ${this._config.title||this._t("card.title")}
this._tabPointerMove(e)}> - ${a.map(e=>W``)}
`:V} - ${this._t("count.devices",{n:u.filter(e=>!e.hidden).length})} + ${this._t("count.devices",{n:p.filter(e=>!e.hidden).length})} - ${this._labsIso&&"view"===this._mode&&!this._kiosk?W``:V}
@@ -5274,19 +5277,19 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef `:V}
${this._canEdit&&!this._kiosk?W`
- ${this._editorRuntime?"plan"===D?this._renderMarkupBar():"devices"===D?this._renderDevicesBar():this._renderDecorBar():V} + style=${A?`opacity:${A.toolbarContentOpacity}`:V}> + ${this._editorRuntime?"plan"===R?this._renderMarkupBar():"devices"===R?this._renderDevicesBar():this._renderDecorBar():V}
`:V}
-
this._markupClick(e)} @wheel=${e=>this._onWheel(e)} @pointerdown=${e=>{this._notePointer(e),this._stagePointerDown(e)}} @@ -5294,141 +5297,123 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef @pointerleave=${e=>this._stagePointerLeave(e)} @pointerup=${e=>this._stagePointerUp(e)} @pointercancel=${e=>this._stagePointerCancel(e)}> - ${nr(O,I)} + ${nr(P,F)} ${this._editorRuntime?this._renderEditorSecondary():V}
- ${h&&_?.structural?j` + ${d&&g?.structural?j``:V} - - + + ${""} - ${this._wallHatchDefs(m.color)}${j`${this._paperShapes(s.rooms).map(e=>"path"in e?j``:"poly"in e?j``:j``)}`} - ${this._editing?this._renderMarkupDefs(l):V} + ${this._wallHatchDefs(_.color)}${j`${this._paperShapes(a.rooms).map(e=>"path"in e?j``:"poly"in e?j``:j``)}`} + ${this._editing?this._renderMarkupDefs(c):V} ${""} - ${this._editing&&!this._markup&&this._gridLevels()?j``:V} - ${s.bg&&A?j`this._onBackdropLoaded(s.bg.href,A)} - transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:V} + ${this._editing&&!this._markup&&this._gridLevels()?j``:V} + ${a.bg&&D?j`this._onBackdropLoaded(a.bg.href,D)} + transform=${a.bg.angle?`rotate(${a.bg.angle} ${a.bg.x+a.bg.w/2} ${a.bg.y+a.bg.h/2})`:V} @dblclick=${e=>this._openBackdropDialog(e)} preserveAspectRatio="none" />`:V} - ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Ht(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||m.showBorders).map(n=>{let o="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=yn(m.fill,n);if(!this._markup&&(m.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${m.color}`,`--room-stroke-op:${m.showBorders?m.opacity:0}`);const t=f.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),a=e.join(";")}let c;const h=e=>{this._roomTipEnabledForPointer(e)&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",y?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n),!0))},d=e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})},u=r(n),p=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),_=u&&!p?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return u.some((e,i)=>Mn(t,[e[0],e[1],...u[(i+1)%u.length]])<=.08*this._gridPitch)}):[],g=p?[]:t,v=_.concat(g);v.length&&(o+=" noedge");const b=this._spaceWalls.length&&n.id&&u&&this._innerRoomContour(s,n.id,e,i)||u,w=b?ci(b,(k=n,s.rooms.filter(e=>e!==k).map(r).filter(Boolean))):[];var k;const x=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",$=b?this._cleanFloor(n,b,s).path:"",S=n.id||V,M=n.area||V,C=$&&b?j`{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Ht(e)),n.get(e));return a.rooms.filter(e=>e.area||"view"===this._mode||this._markup||_.showBorders).map(n=>{let o="room "+(a.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let s="";const l=yn(_.fill,n);if(!this._markup&&(_.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${_.color}`,`--room-stroke-op:${_.showBorders?_.opacity:0}`);const t=v.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),s=e.join(";")}let c;const h=e=>{this._roomTipEnabledForPointer(e)&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",b?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n),!0))},d=e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:a.id,room:n},this._syncLiveHover())},u=r(n),p=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),m=u&&!p?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return u.some((e,i)=>Mn(t,[e[0],e[1],...u[(i+1)%u.length]])<=.08*this._gridPitch)}):[],g=p?[]:t,f=m.concat(g);f.length&&(o+=" noedge");const y=this._spaceWalls.length&&n.id&&u&&this._innerRoomContour(a,n.id,e,i)||u,w=y?ci(y,(k=n,a.rooms.filter(e=>e!==k).map(r).filter(Boolean))):[];var k;const x=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",S=y?this._cleanFloor(n,y,a).path:"",$=n.id||V,M=n.area||V,C=S&&y?j`this._clearTransientHover()}>`:w.length&&b?j`this._clearTransientHover()}>`:w.length&&y?j`this._clearTransientHover()}>`:b&&b!==u?j`this._clearTransientHover()}>`:y&&y!==u?j`this._clearTransientHover()}>`:n.poly?j`this._clearTransientHover()}>`:n.poly?j`this._clearTransientHover()}>`:j`this._clearTransientHover()}>`:j`this._clearTransientHover()}>`,R=v.length&&u?mn(u,v,.02*this._gridPitch):null,T=R?j`this._clearTransientHover()}>`,R=f.length&&u?mn(u,f,.02*this._gridPitch):null,T=R?j``:V;return j`${C}${T}`})})()} - ${this._renderRoomHoverFill(z)} - ${this._renderOpeningTunnelFills(s,f)} - ${this._renderGlowBaseRooms(s,v)} - ${this._renderOpeningTunnelFills(s,v,"glow-base")} + style=${this._markup?V:`stroke:${_.color};stroke-opacity:${_.showBorders?_.opacity:0}`}>`:V;return j`${C}${T}`})})()} + ${this._renderRoomHoverFill(T)} + ${this._renderOpeningTunnelFills(a,v)} + ${this._renderGlowBaseRooms(a,y)} + ${this._renderOpeningTunnelFills(a,y,"glow-base")} ${""} - ${m.hideDecor&&"decor"!==this._mode?V:this._renderDecorLayer()} - ${N?this._renderGlowLayer(s,m):V} - ${this._renderSunRays(s)} + ${_.hideDecor&&"decor"!==this._mode?V:this._renderDecorLayer()} + ${I?this._renderGlowLayer(a,_):V} + ${this._renderSunRays(a)} ${this._editing?j`${this._renderAlignGuides()}`:V} + opacity="${A?.editorWeight??1}">${this._renderAlignGuides()}`:V} ${this._markup?j`${this._renderMarkupLayer(l)}`:V} + opacity="${A?.editorWeight??1}">${this._renderMarkupLayer(c)}`:V} ${""} ${""} - ${this._editing?V:this._renderZeroWalls(m)} - ${this._renderWallBodies(m)} + ${this._editing?V:this._renderZeroWalls(_)} + ${this._renderWallBodies(_)} ${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():V} - ${this._renderRoomHoverOutline(z)} + ${this._renderRoomHoverOutline(T)} ${""} - ${this._editing?this._renderZeroWalls(m):V} + ${this._editing?this._renderZeroWalls(_):V} ${this._markup?j`${this._renderHiddenWallDiagnosticOverlay()}`:V} + opacity="${A?.editorWeight??1}">${this._renderHiddenWallDiagnosticOverlay()}`:V} ${this._markup?j`${this._renderOpeningPlacementPreview()}`:V} - ${M?this._renderOpeningDimensionGuides(M):V} - ${M?.guide?this._renderOpeningCenterTick(M.guide):V} + opacity="${A?.editorWeight??1}">${this._renderOpeningPlacementPreview()}`:V} + ${C?this._renderOpeningDimensionGuides(C):V} + ${C?.guide?this._renderOpeningCenterTick(C.guide):V} ${""} ${""} ${this._markup?j`${this._renderActiveChainInk()}`:V} - ${this._markup?j`${this._renderPlanSnapOverlay()}`:V} - ${m.hideOpenings&&!this._markup||_&&!_.floorSymbols?V:this._renderOpenings(m)} + opacity="${A?.editorWeight??1}">${this._renderActiveChainInk()}`:V} + ${this._markup?j`${this._renderPlanSnapOverlay()}`:V} + ${_.hideOpenings&&!this._markup||g&&!g.floorSymbols?V:this._renderOpenings(_)} ${this._renderWallThickUi()} - ${this._markup&&"resize"===this._tool?this._renderResizeLayer(x):V} + ${this._markup&&"resize"===this._tool?this._renderResizeLayer(S):V} ${""} - ${this._renderBackdropFrame(x)} - ${this._renderTextFrame(x)} + ${this._renderBackdropFrame(S)} + ${this._renderTextFrame(S)} + - ${h&&_?.structural?j` - `:V}${B={hass:this.hass,settings:this._settings,devices:this._renderDevices,registry:this._haRegistry,currentSpace:s.id,viewKey:x,view:"view"===this._mode,kiosk:this._kiosk},B.view&&!B.kiosk&&!0===B.hass?.user?.is_admin&&z_(B.settings).enabled?(import("./hp-zigbee-topology-overlay-B7l7_FNi.js"),W``):V} + ${this._renderIsoWalls(g,f)} + `:V}${H={hass:this.hass,settings:this._settings,devices:this._renderDevices,registry:this._haRegistry,currentSpace:a.id,viewKey:S,view:"view"===this._mode,kiosk:this._kiosk},H.view&&!H.kiosk&&!0===H.hass?.user?.is_admin&&O_(H.settings).enabled?(import("./hp-zigbee-topology-overlay-CnCZWJj6.js"),W``):V} ${""} -
- ${u.map(e=>this._renderDevice(e,x,y))} - ${this._renderVacuums(this._renderVacuumDevices,x,s.id)} - ${this._renderVacFit(x)} - ${this._renderOpeningLocks(x)} - ${m.showNames||this._markup?s.rooms.map(e=>this._renderRoomLabel(e,s,x,m)):V} - ${this._markup?s.rooms.map(e=>this._renderRoomGear(e,s,x)):V} +
+ ${p.map(e=>this._renderDevice(e,S,b))} + ${this._renderVacuums(this._renderVacuumDevices,S,a.id)} + ${this._renderVacFit(S)} + ${this._renderOpeningLocks(S)} + ${_.showNames||this._markup?a.rooms.map(e=>this._renderRoomLabel(e,a,S,_)):V} + ${this._markup?a.rooms.map(e=>this._renderRoomGear(e,a,S)):V}
- ${this._measureAnchor?W`
${this._renderMeasureLabel(x)}
`:V} - ${this._resize?.liveLabels?W`
${this._resize?.liveLabels?.map(e=>W`
${e.text}
`)}
`:V} - ${M?W`
${M.labels.map(e=>W`
${e.text}
`)}
`:V} - ${this._wallDialog?W`
${this._renderWallThickDialog()}
`:V} - ${C?W`
${C.text}
`:V} - ${T?W`
${T.map(e=>W`
${e.text}
`)}
`:V} - ${R?W`
${R.text}
`:V} +
+ ${this._wallDialog?W`
${this._renderWallThickDialog()}
`:V}
- ${this._zoom>1?W`
${Math.round(100*this._zoom)}%
`:V} + ${this._renderFarHint()} ${this._renderHomeArrow()} ${this._renderEditorRuntimeLoading()} @@ -5480,13 +5465,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef ${this._backupExportDialog&&this._editorRuntime?this._renderBackupExportDialog():V} ${this._backupImportDialog&&this._editorRuntime?this._renderBackupImportDialog():V} ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():V} - ${this._tip?W`
- ${this._tip.title}${this._tip.meta?W`${this._tip.meta}`:V} - ${null!=this._tip.temp?W`${this._t("tip.temp_avg")} ${this._tip.temp}°`:V} - ${null!=this._tip.hum?W`${this._t("tip.hum_avg")} ${this._tip.hum}%`:V} - ${null!=this._tip.lqi?W`${this._t("tip.lqi")} - ${this._tip.lqi}`:V} -
`:V} + ${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?W`
${this._model.map(e=>W``)}
`:V} @@ -5520,16 +5499,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef `:V} ${this._toast?W``:V} - `;var B}_vacCandidateStatus(e,t,i=this._planHass){const n=this._bindingStatus("entity:"+e);if("ha_disabled"===n.kind)return"disabled";if("orphaned"===n.kind)return"missing";if("unverified"===n.kind)return"unverified";const r=i?.states?.[e];return"unavailable"===r?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,n]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const r=ic(e,n,t[e]);r&&i.push(r)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const n=e.marker?.vacuum,r="string"==typeof n?.source&&!!n.source,o=new Set(e.entities||[]),s=new Set(o);r&&s.add(n.source);const a=i?.entities||{},l=[];for(const e of s){const t=i?.states?.[e],o=ic(e,t,a[e]);o?l.push(o):r&&e===n.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:a[e]?.platform?String(a[e].platform):null,category:e.startsWith("camera.")?"camera":"partial",hasPosition:!1,hasRooms:!1,hasPath:!1,hasMapId:!1,score:0})}if(t&&this._vacAllCameraCache?.devId===e.id){const e=new Set(l.map(e=>e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Vh(this._planHass,this._devices,null,this._virtualLights)){const i=rp(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:ap(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=c_(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),d_(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Vh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==ki(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&h_(a,s,o,window.clearTimeout.bind(window)):(a=c_(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==ki(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=u_(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===ki(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=ac(this.hass.states[i||""]?.state),r=Ql(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==pc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=sc(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=sc(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){const n=this._vacEntity(e),r=n?i?.states?.[n]?.attributes?.selected_map:null;return o=t.mapId,s=r,"default"!==o?o:null!=s?String(s):"default";var o,s}_vacObservedMapId(e,t,i=this._planHass){const n=Ql(i?.states?.[t]?.attributes);return n?this._vacMapId(e,n,i):void 0}_vacSaveMatrix(e,t,i,n,r=""){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n,r)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):V}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_vacTrailPathD(e,t){const i=e.map(e=>e.map(([e,i])=>ql(t,e,i))),n=function(e,t,i=console.warn){if(!Number.isFinite(t)||t<=0)return[];const n=[];let r=0;for(const i of e){if(!i.every(Zl)){r++;continue}const e=i.filter((e,t)=>0===t||!Jl(e,i[t-1]));if(e.length<2)continue;const o=[{kind:"move",point:e[0]}];if(2!==e.length){for(let i=1;i0&&i(`[houseplan] vacuum trail: ${r} segment(s) dropped — non-finite point (check map calibration)`),n}(i,this._cmToUnits(17.5)),r=e=>{const t=this._scenePoint(e);return`${t[0].toFixed(1)} ${t[1].toFixed(1)}`};return n.map(e=>e.map(e=>"move"===e.kind?`M ${r(e.point)}`:"line"===e.kind?`L ${r(e.point)}`:`Q ${r(e.control)} ${r(e.point)}`).join(" ")).join(" ")}_renderVacuums(e,t,i){if(this._markup||"decor"===this._mode)return V;const n=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,r=this._vacJumpOnce||n!==this._vacViewKey;this._vacViewKey=n,this._vacJumpOnce=!1;const o=[],s=[];for(const n of e){if(n.hidden||!this._isVacDev(n))continue;if("static_icon"===ki(n.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${n.id}`),a=e?.source??this._vacSource(n,this._renderPlanHass);if(!a)continue;const l=e?.telemetry??Ql(this._renderPlanHass?.states[a]?.attributes);if(!l)continue;const c=e?.routes??fc(n.id,n.marker?.vacuum??null,n.space,a),h=e?.resolution??yc({routes:c,observed:bc(c,[a],e=>this._vacObservedMapId(n,e)),spaceIds:new Set(this._model.map(e=>e.id))}),d=e?.server??this._vacSrvTrails[n.id],u=kc({resolution:h,routes:c,renderSpace:i,rootSource:n.marker?.vacuum?.source??null,serverCurrent:d?.current??null,serverPrevious:d?.previous??null,explicitRoutes:Array.isArray(n.marker?.vacuum?.map_routes)});if(!u.live&&!u.previous)continue;const p=u.live,m=e?.runtime??this._vacRt.get(n.id),_=m?.moving??!1,g=pc(n.marker?.vacuum),f="always"===g||"cleaning"===g&&_,v=d,y=u.currentRunMatches&&Array.isArray(v?.current?.points)?v.current:null,b=u.previous&&Array.isArray(v?.previous?.points)?v.previous:null;if("always"===g&&b&&u.previous){const e=Kl(b.points),t=this._vacTrailPathD(e,u.previous);t&&s.push(j``)}if(f&&p){const e=Yl(l,y,m?.trail||[]),t=!_||"integration"!==e.source&&"server"!==e.source?e.path:Xl(e.path);if(t.length){const e=this._vacTrailPathD(t,p);e&&s.push(j``);const i=t[t.length-1];if(_&&i?.length>=2){const e=i[i.length-1],[t,r]=ql(p,e[0],e[1]),o=this._scenePoint([t,r]),a=o[0].toFixed(1),l=o[1].toFixed(1);s.push(j``)}}}if(!_||!l.pos||!p)continue;const[w,k]=ql(p,l.pos.x,l.pos.y),x=this._scenePoint([w,k]),$=(x[0]-t.x)/t.w*100,S=(x[1]-t.y)/t.h*100,M=m&&m.lastTs>0&&Date.now()-m.lastTs>6e4,C=n.marker?.icon||n.icon||"mdi:robot-vacuum";o.push(W`
e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Kh(this._planHass,this._devices,null,this._virtualLights)){const i=sp(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:cp(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=d_(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),p_(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Kh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==ki(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&u_(a,s,o,window.clearTimeout.bind(window)):(a=d_(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==ki(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=m_(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===ki(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=lc(this.hass.states[i||""]?.state),r=ec(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==mc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=ac(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=ac(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){const n=this._vacEntity(e),r=n?i?.states?.[n]?.attributes?.selected_map:null;return o=t.mapId,s=r,"default"!==o?o:null!=s?String(s):"default";var o,s}_vacObservedMapId(e,t,i=this._planHass){const n=ec(i?.states?.[t]?.attributes);return n?this._vacMapId(e,n,i):void 0}_vacSaveMatrix(e,t,i,n,r=""){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n,r)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):V}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_vacTrailPathD(e,t){const i=e.map(e=>e.map(([e,i])=>Wl(t,e,i))),n=function(e,t,i=console.warn){if(!Number.isFinite(t)||t<=0)return[];const n=[];let r=0;for(const i of e){if(!i.every(Jl)){r++;continue}const e=i.filter((e,t)=>0===t||!Ql(e,i[t-1]));if(e.length<2)continue;const o=[{kind:"move",point:e[0]}];if(2!==e.length){for(let i=1;i0&&i(`[houseplan] vacuum trail: ${r} segment(s) dropped — non-finite point (check map calibration)`),n}(i,this._cmToUnits(17.5)),r=e=>{const t=this._scenePoint(e);return`${t[0].toFixed(1)} ${t[1].toFixed(1)}`};return n.map(e=>e.map(e=>"move"===e.kind?`M ${r(e.point)}`:"line"===e.kind?`L ${r(e.point)}`:`Q ${r(e.control)} ${r(e.point)}`).join(" ")).join(" ")}_renderVacuums(e,t,i){if(this._markup||"decor"===this._mode)return V;const n=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,r=this._vacJumpOnce||n!==this._vacViewKey;this._vacViewKey=n,this._vacJumpOnce=!1;const o=[],s=[];for(const n of e){if(n.hidden||!this._isVacDev(n))continue;if("static_icon"===ki(n.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${n.id}`),a=e?.source??this._vacSource(n,this._renderPlanHass);if(!a)continue;const l=e?.telemetry??ec(this._renderPlanHass?.states[a]?.attributes);if(!l)continue;const c=e?.routes??vc(n.id,n.marker?.vacuum??null,n.space,a),h=e?.resolution??bc({routes:c,observed:wc(c,[a],e=>this._vacObservedMapId(n,e)),spaceIds:new Set(this._model.map(e=>e.id))}),d=e?.server??this._vacSrvTrails[n.id],u=xc({resolution:h,routes:c,renderSpace:i,rootSource:n.marker?.vacuum?.source??null,serverCurrent:d?.current??null,serverPrevious:d?.previous??null,explicitRoutes:Array.isArray(n.marker?.vacuum?.map_routes)});if(!u.live&&!u.previous)continue;const p=u.live,m=e?.runtime??this._vacRt.get(n.id),_=m?.moving??!1,g=mc(n.marker?.vacuum),f="always"===g||"cleaning"===g&&_,v=d,y=u.currentRunMatches&&Array.isArray(v?.current?.points)?v.current:null,b=u.previous&&Array.isArray(v?.previous?.points)?v.previous:null;if("always"===g&&b&&u.previous){const e=Yl(b.points),t=this._vacTrailPathD(e,u.previous);t&&s.push(j``)}if(f&&p){const e=Xl(l,y,m?.trail||[]),t=!_||"integration"!==e.source&&"server"!==e.source?e.path:Zl(e.path);if(t.length){const e=this._vacTrailPathD(t,p);e&&s.push(j``);const i=t[t.length-1];if(_&&i?.length>=2){const e=i[i.length-1],[t,r]=Wl(p,e[0],e[1]),o=this._scenePoint([t,r]),a=o[0].toFixed(1),l=o[1].toFixed(1);s.push(j``)}}}if(!_||!l.pos||!p)continue;const[w,k]=Wl(p,l.pos.x,l.pos.y),x=this._scenePoint([w,k]),S=(x[0]-t.x)/t.w*100,$=(x[1]-t.y)/t.h*100,M=m&&m.lastTs>0&&Date.now()-m.lastTs>6e4,C=n.marker?.icon||n.icon||"mdi:robot-vacuum";o.push(W`
{e.stopPropagation();const t=this._vacEntity(n);t&&this._openMoreInfo(t)}}>
`)}return this._vacLastView=t,o.length&&!this._vacRaf&&this._vacRafLoop(),o.length||s.length?W` - ${s.length?j`${s}`:V} - ${o}`:V}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,..._p(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Ju(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",zu(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),m=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return W`
${s}`:V} + ${o}`:V}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,...fp(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=ep(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",Pu(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),m=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return W`
this._clickDevice(t,e)} @keydown=${t=>this._keyDevice(t,e)} @@ -5555,10 +5534,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef @pointercancel=${t=>this._pointerCancel(t,e)} @lostpointercapture=${t=>this._pointerCancel(t,e)} > - ${fp(a,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:a.haDisabled?h:""})} + ${yp(a,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:a.haDisabled?h:""})} ${this._vacRouteBadge(e)}
`}_vacRouteBadge(e){if(!this._isVacDev(e)||e.hidden)return V;if("static_icon"===ki(e.marker?.display))return V;const t=this._renderDeviceSnapshot?.facts.get(`vacuum:${e.id}`),i=function(e,t){if(!t||!e)return null;switch(e.kind){case"unmapped":case"needs_calibration":case"ambiguous":case"missing_space":return e.kind;default:return null}}(t?.resolution,this._vacRt.get(e.id)?.moving??!1);if(!i)return V;const n=this._t(`vac.route_warn_${i}`);return W` - `}_roomArea(e){const t=Ht(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return zn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return pd(this._renderPlanHass,t,"temp",this._markers);const i=_d(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return pd(this._renderPlanHass,t,"hum",this._markers);const i=_d(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache,n=this._settings.exclude_integrations;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e&&i.ex===n)return i.m;const r=vd(t,this._iconRules,e,this._excluded);return this._climateCache={h:t,r:this._iconRules,mk:e,ex:n,m:r},r}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*_f,y:i.y*_f};const n=this._roomCenter(e),r=this._gridPitch;return{x:Ea(Ot(n[0],r)),y:Ea(Ot(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return V;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(W`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(W`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(W`${t}`)}if(n.labelLight){const t=(d=Vh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(W`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return W`
`}_roomArea(e){const t=Ht(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return zn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return _d(this._renderPlanHass,t,"temp",this._markers);const i=fd(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return _d(this._renderPlanHass,t,"hum",this._markers);const i=fd(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache,n=this._settings.exclude_integrations;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e&&i.ex===n)return i.m;const r=bd(t,this._iconRules,e,this._excluded);return this._climateCache={h:t,r:this._iconRules,mk:e,ex:n,m:r},r}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*yf,y:i.y*yf};const n=this._roomCenter(e),r=this._gridPitch;return{x:Na(Ot(n[0],r)),y:Na(Ot(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return V;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(W`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(W`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(W`${t}`)}if(n.labelLight){const t=(d=Kh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(W`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return W`
- ${this._fmtLen(t,i)} · ${s}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=$n(e.a,e.b);return{x:o,y:s,on45:Sn(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${zn(hr(Math.abs(n-t),this._cellCm,this._gridPitch)*hr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._deviceDrag?.moved){const e=this._devices.find(e=>e.id===this._deviceDrag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=_f,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]:null;return Ut(e.type,t?.state,!!e.invert,t?.attributes?.current_position)}_planEntityAvailable(e){return!!e&&(!ad(this._fullRegistryHass,e,sd(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!ad(this._renderPlanHass,e,sd(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=ph(e)){return!!t&&"active"===wh(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return j``;const i=this._spaceModel();if(!i)return j``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return j`${t.map(e=>{if(e.orphanReason)return j``}_renderLiveEditorMeasurements(e){const t=e,i=this._opMeasureView,n=this._decorMeasure,r=this._bdLive,o=this._editorRuntime?._furnLive()??null;return W` + ${this._measureAnchor?W`
${this._renderMeasureLabel(t)}
`:V} + ${this._resize?.liveLabels?W`
${this._resize.liveLabels.map(e=>W`
${e.text}
`)}
`:V} + ${i?W`
${i.labels.map(e=>W`
${e.text}
`)}
`:V} + ${n?W`
${n.text}
`:V} + ${o?W`
${o.map(e=>W`
${e.text}
`)}
`:V} + ${r?W`
${r.text}
`:V} + `}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(e){const t=this._measureAnchor,i=this._cursorPt,n=(i[0]-e.x)/e.w*100,r=(i[1]-e.y)/e.h*100,o=Sn(t,i),s=Math.round(10*o)/10,a=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const n=Math.abs(t[0]-e[0]),r=Math.abs(t[1]-e[1]);if(!Number.isFinite(n)||!Number.isFinite(r)||Math.hypot(n,r)<=i)return!1;const o=Math.max(n,r,1);return n<=i*o||r<=i*o||Math.abs(n-r)<=i*o}(t,i,2e-4*this._gridPitch);return W`
+ ${this._fmtLen(t,i)} · ${s}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=Sn(e.a,e.b);return{x:o,y:s,on45:$n(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${zn(hr(Math.abs(n-t),this._cellCm,this._gridPitch)*hr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._deviceDrag?.moved){const e=this._devices.find(e=>e.id===this._deviceDrag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=yf,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]:null;return Ut(e.type,t?.state,!!e.invert,t?.attributes?.current_position)}_planEntityAvailable(e){return!!e&&(!cd(this._fullRegistryHass,e,ld(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!cd(this._renderPlanHass,e,ld(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=mh(e)){return!!t&&"active"===kh(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e,t){const i=t?this._openingsR.filter(e=>t.includes(e.id)):this._openingsR;if(!i.length)return j``;const n=this._spaceModel();if(!n)return j``;const r=e.color,o=this._spaceWalls,s=this._openCuts(),a=this._openingWallIndexFor(n,s).value;return j`${i.map(e=>{if(e.orphanReason)return j`{t.stopPropagation(),this._editOpening(e)}}> - + ! - `;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":n,o="gate"===e.type?!e.flip_v:e.flip_v,a=e.partitionHost||r.length||"gate"===e.type?this._openingFace(e,s,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:n,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=$u(l),u=ya(10,this._cellCm),p=ya(12,this._cellCm);return j``;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":r,n="gate"===e.type?!e.flip_v:e.flip_v,s=e.partitionHost||o.length||"gate"===e.type?this._openingFace(e,a,!!n):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:r,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s},{half:c,outlineHalf:h,hitHalf:d}=Mu(l),u=ba(10,this._cellCm),p=ba(12,this._cellCm);return j` - ${Tu(l)} + ${zu(l)} + rx="${ba(6,this._cellCm)}"> this._opClick(t,e)} @@ -5600,7 +5595,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef @pointermove=${t=>this._opPointerMove(t,e)} @pointerup=${t=>this._opPointerUp(t,e)} @pointercancel=${t=>this._opPointerUp(t,e)}> - `})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return W`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=ya(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,m=(u[1]-e.y)/e.h*100;return W`
`})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return W`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=ba(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,m=(u[1]-e.y)/e.h*100;return W`
{"view"===this._mode&&(e.stopPropagation(),this._openingInfo=t)}}>
- `}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Vh(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?Ai(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(dn).filter(e=>this._planEntityAvailable(e));return W``}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Kh(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?Ai(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(dn).filter(e=>this._planEntityAvailable(e));return W`
${(()=>{const t=this._cardEntities(e);return t.length?W`
${t.map(({eid:e,kind:t})=>{const i=this.hass.states[e],n=this.hass.entities[e]?.name||i?.attributes?.friendly_name||e,r=i?Ai(this.hass,e)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return W`
- + ${n} ${"toggle"===t?W``:"open"===t?W`
- `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return qd({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){const i=this._planHass?.states?.[e]||this.hass?.states?.[e];try{return i&&"function"==typeof this.hass?.formatEntityState?this.hass.formatEntityState(i):t}catch{return t}}_toggleConfirmationStateText(e){const t=String(e.state||"unknown"),i=e.entityId?this._toggleStateText(e.entityId,t):t;if(i.trim().toLocaleLowerCase()!==t.trim().toLocaleLowerCase())return i;const n={on:"confirm.state_on",off:"confirm.state_off",open:"confirm.state_open",closed:"confirm.state_closed",opening:"confirm.state_opening",closing:"confirm.state_closing",unknown:"confirm.state_unknown"}[t];return n?this._t(n):t.replaceAll("_"," ").replaceAll("-"," ")}_toggleConfirmationLines(e){const t={"turn-on":"confirm.state_on","turn-off":"confirm.state_off",open:"confirm.state_open",close:"confirm.state_closed",stop:"confirm.state_stopped"};return function(e,t){if(!jd(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,n=i?e.targets.filter(e=>"on"===e.state).length:0,r=i?0===n?t.groupAllOff():t.groupCurrent(n,e.targets.length):t.state(e.targets[0]);let o;return o="toggle"===e.nextEffect?t.expectedByHa():i&&"turn-on"===e.nextEffect?t.groupAllOn():i&&"turn-off"===e.nextEffect?t.groupAllOff():t.effect(e.nextEffect),[t.current(r),t.expected(o),...e.skippedTargets.length?[t.unavailable(e.skippedTargets.length)]:[]]}(e,{state:e=>this._toggleConfirmationStateText(e),current:e=>this._t("confirm.current_state",{state:e}),expected:e=>this._t("confirm.expected_state",{state:e}),groupCurrent:(e,t)=>this._t("confirm.group_current",{on:e,total:t}),groupAllOn:()=>this._t("confirm.group_all_on"),groupAllOff:()=>this._t("confirm.group_all_off"),unavailable:e=>this._t("confirm.unavailable_targets",{count:e}),effect:e=>this._t(t[e]),expectedByHa:()=>this._t("confirm.expected_by_ha")})}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return W`
+ `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return jd({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){const i=this._planHass?.states?.[e]||this.hass?.states?.[e];try{return i&&"function"==typeof this.hass?.formatEntityState?this.hass.formatEntityState(i):t}catch{return t}}_toggleConfirmationStateText(e){const t=String(e.state||"unknown"),i=e.entityId?this._toggleStateText(e.entityId,t):t;if(i.trim().toLocaleLowerCase()!==t.trim().toLocaleLowerCase())return i;const n={on:"confirm.state_on",off:"confirm.state_off",open:"confirm.state_open",closed:"confirm.state_closed",opening:"confirm.state_opening",closing:"confirm.state_closing",unknown:"confirm.state_unknown"}[t];return n?this._t(n):t.replaceAll("_"," ").replaceAll("-"," ")}_toggleConfirmationLines(e){const t={"turn-on":"confirm.state_on","turn-off":"confirm.state_off",open:"confirm.state_open",close:"confirm.state_closed",stop:"confirm.state_stopped"};return function(e,t){if(!Vd(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,n=i?e.targets.filter(e=>"on"===e.state).length:0,r=i?0===n?t.groupAllOff():t.groupCurrent(n,e.targets.length):t.state(e.targets[0]);let o;return o="toggle"===e.nextEffect?t.expectedByHa():i&&"turn-on"===e.nextEffect?t.groupAllOn():i&&"turn-off"===e.nextEffect?t.groupAllOff():t.effect(e.nextEffect),[t.current(r),t.expected(o),...e.skippedTargets.length?[t.unavailable(e.skippedTargets.length)]:[]]}(e,{state:e=>this._toggleConfirmationStateText(e),current:e=>this._t("confirm.current_state",{state:e}),expected:e=>this._t("confirm.expected_state",{state:e}),groupCurrent:(e,t)=>this._t("confirm.group_current",{on:e,total:t}),groupAllOn:()=>this._t("confirm.group_all_on"),groupAllOff:()=>this._t("confirm.group_all_off"),unavailable:e=>this._t("confirm.unavailable_targets",{count:e}),effect:e=>this._t(t[e]),expectedByHa:()=>this._t("confirm.expected_by_ha")})}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return W`
${this._t("preview.room_name")} @@ -5673,4 +5668,4 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef 45% · ${this._t("roomcard.light_partial",{on:1,total:3})} -
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}kf.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_backdropGuard:{state:!0},_tapConfirm:{state:!0},_dangerConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_decorImagePalette:{state:!0},_decorAssetCatalog:{state:!0},_decorAssetBusy:{state:!0},_furnCategory:{state:!0},_furnPreviewInput:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_supportDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},kf.ZOOM_MAX=8,kf.ZOOM_MIN=1/3,kf.styles=[su,k_],customElements.get("houseplan-card")||customElements.define("houseplan-card",kf),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${tf} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{nu as $,V as A,wr as B,ai as C,li as D,U as E,Ct as F,hi as G,kl as H,Na as I,yl as J,gl as K,hs as L,Go as M,xa as N,sd as O,ad as P,Bt as Q,pn as R,Mn as S,qt as T,Fa as U,Aa as V,Ht as W,Zd as X,ou as Y,ds as Z,no as _,fc as a,Fp as a$,Yd as a0,Po as a1,ps as a2,Jd as a3,Ns as a4,Bs as a5,po as a6,Xt as a7,io as a8,vo as a9,Rl as aA,Da as aB,Tl as aC,Jo as aD,Is as aE,Io as aF,Vs as aG,ga as aH,Op as aI,Ks as aJ,us as aK,z_ as aL,Ki as aM,Qn as aN,Jn as aO,Zn as aP,Gi as aQ,ve as aR,xu as aS,eg as aT,uo as aU,Ea as aV,Ot as aW,It as aX,kr as aY,Ys as aZ,Ll as a_,xp as aa,$p as ab,ts as ac,Nr as ad,Ro as ae,Mo as af,Jr as ag,eo as ah,Fr as ai,Pp as aj,Do as ak,Pa as al,Co as am,Sa as an,Ma as ao,Ii as ap,Ei as aq,Di as ar,zi as as,Ta as at,am as au,cr as av,j as aw,om as ax,ll as ay,ml as az,dc as b,Oo as b$,to as b0,x_ as b1,cs as b2,Ps as b3,fs as b4,Ep as b5,ti as b6,Zt as b7,_i as b8,ma as b9,Sl as bA,gr as bB,Ml as bC,bl as bD,_l as bE,Cl as bF,xl as bG,$l as bH,em as bI,Fo as bJ,lo as bK,ya as bL,$u as bM,iu as bN,Zg as bO,tu as bP,qs as bQ,Vg as bR,Jg as bS,Sp as bT,jt as bU,Qd as bV,ru as bW,ui as bX,Qt as bY,Wt as bZ,ei as b_,ta as ba,ea as bb,zo as bc,Ao as bd,Os as be,Et as bf,il as bg,da as bh,ha as bi,Dn as bj,zn as bk,ri as bl,Zo as bm,Xo as bn,Hp as bo,Lp as bp,_r as bq,yr as br,vr as bs,hr as bt,sr as bu,lr as bv,pr as bw,$n as bx,or as by,ar as bz,W as c,qh as c$,gi as c0,an as c1,Wu as c2,ju as c3,Ah as c4,Nd as c5,ki as c6,Ci as c7,Tn as c8,wn as c9,rc as cA,pc as cB,Vl as cC,oc as cD,ec as cE,tc as cF,uc as cG,cc as cH,ql as cI,kn as cJ,xn as cK,Ut as cL,Gg as cM,Tu as cN,Ug as cO,rl as cP,ms as cQ,qo as cR,Wo as cS,fe as cT,fd as cU,Uu as cV,Vu as cW,ud as cX,Gd as cY,Gh as cZ,Vh as c_,nd as ca,Kp as cb,Qr as cc,od as cd,id as ce,Vi as cf,ji as cg,Wi as ch,wa as ci,Dp as cj,qi as ck,Bi as cl,Li as cm,Hi as cn,ba as co,jp as cp,Np as cq,ur as cr,Yi as cs,A_ as ct,qr as cu,er as cv,ye as cw,$e as cx,be as cy,Ql as cz,$_ as d,Fh as d0,dn as d1,zh as d2,rd as d3,ln as d4,cp as d5,Pu as d6,Wh as d7,jh as d8,Oh as d9,Pd as da,xi as db,wi as dc,Fu as dd,bi as de,ka as df,$i as dg,Si as dh,ro as di,Yt as dj,cd as dk,dd as dl,za as dm,le as dn,Bl as dp,bu as dq,hu as dr,_u as ds,du as dt,Ft as du,kf as dv,cm as e,hc as f,Il as g,S_ as h,hm as i,o as j,he as k,wu as l,Dt as m,_c as n,bc as o,su as p,mp as q,yc as r,Ni as s,ku as t,_p as u,zu as v,Ku as w,fp as x,D_ as y,ni as z}; +
`}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}Mf.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_backdropGuard:{state:!0},_tapConfirm:{state:!0},_dangerConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_decorImagePalette:{state:!0},_decorAssetCatalog:{state:!0},_decorAssetBusy:{state:!0},_furnCategory:{state:!0},_furnPreviewInput:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_supportDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0}},Mf.ZOOM_MAX=8,Mf.ZOOM_MIN=1/3,Mf.styles=[lu,S_],customElements.get("houseplan-card")||customElements.define("houseplan-card",Mf),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${sf} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{cd as $,V as A,Xu as B,gp as C,le as D,U as E,fp as F,Pu as G,yp as H,F_ as I,ni as J,wr as K,ai as L,li as M,Sa as N,Ct as O,hi as P,Oa as Q,xl as R,Ha as S,bl as T,fl as U,hs as V,Go as W,cs as X,Ps as Y,fs as Z,ld as _,vc as a,$u as a$,Bt as a0,pn as a1,Mn as a2,qt as a3,Pa as a4,Ht as a5,Qd as a6,au as a7,ds as a8,no as a9,Ei as aA,Di as aB,zi as aC,Da as aD,cm as aE,cr as aF,am as aG,cl as aH,_l as aI,Tl as aJ,za as aK,Dl as aL,Jo as aM,Is as aN,Io as aO,Gs as aP,fa as aQ,Ep as aR,Ys as aS,us as aT,O_ as aU,Ki as aV,Qn as aW,Jn as aX,Zn as aY,Gi as aZ,ve as a_,ou as aa,Zd as ab,Po as ac,ps as ad,eu as ae,Hs as af,qs as ag,Xt as ah,io as ai,vo as aj,$p as ak,Mp as al,ts as am,Nr as an,Ro as ao,Mo as ap,Jr as aq,eo as ar,Fr as as,Op as at,Do as au,Fa as av,Co as aw,Ma as ax,Ca as ay,Ii as az,uc as b,ui as b$,rg as b0,uo as b1,Na as b2,Ot as b3,It as b4,kr as b5,Xs as b6,Bl as b7,Ip as b8,to as b9,sr as bA,lr as bB,pr as bC,Sn as bD,or as bE,ar as bF,Ml as bG,gr as bH,Cl as bI,wl as bJ,gl as bK,Rl as bL,Sl as bM,$l as bN,im as bO,Fo as bP,Mu as bQ,ru as bR,tf as bS,nu as bT,Ws as bU,Xg as bV,nf as bW,Cp as bX,jt as bY,tu as bZ,su as b_,$_ as ba,Hp as bb,ti as bc,Zt as bd,_i as be,_a as bf,ia as bg,ta as bh,zo as bi,Ao as bj,Os as bk,Et as bl,nl as bm,ua as bn,da as bo,Dn as bp,zn as bq,ri as br,Zo as bs,Xo as bt,Bp as bu,qp as bv,_r as bw,yr as bx,vr as by,hr as bz,W as c,md as c$,Qt as c0,Wt as c1,ei as c2,Oo as c3,gi as c4,an as c5,Uu as c6,Vu as c7,Fh as c8,Ld as c9,ye as cA,Se as cB,be as cC,ec as cD,oc as cE,mc as cF,Gl as cG,sc as cH,tc as cI,ic as cJ,pc as cK,hc as cL,Wl as cM,kn as cN,xn as cO,Ut as cP,Zg as cQ,zu as cR,Yg as cS,ol as cT,ms as cU,qo as cV,Wo as cW,fe as cX,yd as cY,Gu as cZ,Ku as c_,ki as ca,Ci as cb,Tn as cc,wn as cd,od as ce,Xp as cf,Qr as cg,ad as ch,rd as ci,Vi as cj,ji as ck,Wi as cl,ka as cm,Ap as cn,qi as co,Bi as cp,Li as cq,Hi as cr,wa as cs,Vp as ct,Lp as cu,ur as cv,Yi as cw,I_ as cx,qr as cy,er as cz,M_ as d,Yd as d0,Yh as d1,Kh as d2,jh as d3,Ih as d4,dn as d5,Ph as d6,sd as d7,ln as d8,dp as d9,Ou as da,Uh as db,Vh as dc,Eh as dd,Od as de,xi as df,wi as dg,Iu as dh,bi as di,xa as dj,Si as dk,$i as dl,ro as dm,Yt as dn,dd as dp,pd as dq,Aa as dr,ql as ds,ku as dt,uu as du,fu as dv,pu as dw,Ft as dx,Mf as dy,dm as e,dc as f,El as g,C_ as h,um as i,Es as j,ao as k,xu as l,co as m,gc as n,wc as o,lo as p,ba as q,bc as r,Ni as s,po as t,o as u,he as v,j as w,Dt as x,lu as y,Su as z}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js new file mode 100644 index 00000000..31ffd1a4 --- /dev/null +++ b/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js @@ -0,0 +1,2939 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{e as t,i as e,E as s,s as i,a as o,n as a,f as n,b as r,l,o as h,r as c,A as d,c as p,d as u,g as _,h as m,D as g,w as f,N as b,j as v,k as y,m as $,p as w,q as k,t as x,u as S,v as M,x as D,y as C,z as P,B as I,C as T,F as z,G as R,H as A,I as F,J as N,K as H,L,M as E,O,P as B,Q as W,R as j,S as G,T as q,U,V as K,W as V,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as lt,a9 as ht,aa as ct,ab as dt,ac as pt,ad as ut,ae as _t,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as zt,ax as Rt,ay as At,az as Ft,aA as Nt,aB as Ht,aC as Lt,aD as Et,aE as Ot,aF as Bt,aG as Wt,aH as jt,aI as Gt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as le,a$ as he,b0 as ce,b1 as de,b2 as pe,b3 as ue,b4 as _e,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as ze,bn as Re,bo as Ae,bp as Fe,bq as Ne,br as He,bs as Le,bt as Ee,bu as Oe,bv as Be,bw as We,bx as je,by as Ge,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as ls,bR as hs,bS as cs,bT as ds,bU as ps,bV as us,bW as _s,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as zs,cd as Rs,ce as As,cf as Fs,cg as Ns,ch as Hs,ci as Ls,cj as Es,ck as Os,cl as Bs,cm as Ws,cn as js,co as Gs,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as li,cH as hi,cI as ci,cJ as di,cK as pi,cL as ui,cM as _i,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di,c_ as Ci,c$ as Pi,d0 as Ii,d1 as Ti,d2 as zi,d3 as Ri,d4 as Ai,d5 as Fi,d6 as Ni,d7 as Hi,d8 as Li,d9 as Ei,da as Oi,db as Bi,dc as Wi,dd as ji,de as Gi,df as qi,dg as Ui,dh as Ki,di as Vi,dj as Ji,dk as Yi,dl as Xi,dm as Zi,dn as Qi,dp as to,dq as eo,dr as so}from"./houseplan-card-Csq_DR2Y.js";import{F as io,f as oo,p as ao,c as no,i as ro,e as lo,r as ho,a as co,b as po,s as uo,d as _o,t as mo}from"./backdrop-pick-CEZBr4qx.js";import{t as go,m as fo,T as bo}from"./zigbee-topology-D9L1ESh4.js";const vo={},yo=t(class extends e{constructor(){super(...arguments),this.ot=vo}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}});var $o={"gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.show_room_tooltip":"Show the room information window on hover","support.about_group":"About the card","support.guide_group":"Documentation","support.guide":"User guide","support.form_group":"Send a report or suggestion","support.contact":"Contact details (email/tg/WhatsApp), optional.","support.message":"Message","support.attach":"Attach anonymized information from your plan","support.attach_hint":"Includes only allowed plan geometry, settings and bounded runtime facts. Excludes names, Home Assistant IDs, live states, URLs, files, and your message/contact.","support.geometry_warning":"The package contains no names or Home Assistant IDs, but it does contain the exact geometry and dimensions of your home.","support.close_busy_title":"Stop the current operation?","support.close_busy_body":"House Plan is preparing or sending the report. Closing now stops waiting for the result.","support.building":"Preparing anonymized data…","support.preview_summary":"Package v{version} · {spaces} spaces · {size} KiB","support.prepared":"Snapshot prepared {n} min ago","support.show_data":"Show data","support.download":"Download JSON","support.refresh":"Refresh snapshot","support.privacy":"When you send, your message, optional contact and, if selected, exact geometry leave your Home Assistant through the House Plan project relay and are visible to maintainers. The project node keeps the report for up to 30 days. Network infrastructure sees your HA server address, but House Plan does not retain it raw; rate-limit and idempotency metadata is kept for up to 24 hours.","support.error_title":"The report was not sent","support.manual_recovery":"You can keep the message and attachment and continue manually:","support.copy_message":"Copy message","support.success":"Report sent: {id}. Save this number when contacting support.","support.copy_id":"Copy ID","support.sending":"Sending…","support.retry":"Retry","support.send":"Send","support.update_required":"Update the House Plan card and integration to compatible versions to send reports.","support.message_copied":"Message copied","support.id_copied":"Report ID copied","support.copy_failed":"Could not access the clipboard. Select and copy the text manually.","support.validation.message_required":"Enter a message.","support.validation.message_too_long":"The message must contain no more than 10,000 characters.","support.validation.contact_too_long":"Contact details must contain no more than 320 characters.","support.validation.preview_missing":"Wait for the attachment preview or turn the attachment off.","support.validation.preview_expired":"The attachment preview expired. Refresh the snapshot.","support.error.support_invalid_message":"Check the message and try again.","support.error.support_preview_expired":"The attachment preview expired or is no longer available. Refresh the snapshot.","support.error.support_package_too_large":"The anonymized package exceeds the 8 MiB limit.","support.error.support_rate_limited":"Too many reports or previews were requested. Try again later.","support.error.support_unavailable":"The private support service is temporarily unavailable. Your draft remains here.","support.error.support_rejected":"The report did not pass the support service validation.","support.error.unauthorized":"You do not have permission to send this report.","vac.routes_section":"Maps and floors","vac.routes_current_map":"Current map","vac.routes_no_map":"no telemetry","vac.routes_status":"Routing","vac.routes_state_ready":"map assigned and calibrated","vac.routes_state_needs_calibration":"map assigned, calibration needed","vac.routes_state_unmapped":"current map is not assigned to a floor","vac.routes_state_ambiguous":"two maps are indistinguishable — no floor is chosen","vac.routes_state_missing_space":"the map's space was deleted","vac.routes_state_none":"no map data","vac.routes_empty":"No map is assigned to a floor yet.","vac.route_map_default":"no map id","vac.route_active":"Active","vac.route_space":"Space","vac.route_space_none":"not selected","vac.route_status_ready":"ready","vac.route_status_needs_calibration":"needs calibration","vac.route_status_ambiguous":"ambiguous","vac.route_status_missing_space":"space deleted","vac.route_missing_space_group":"Deleted space","vac.route_add_current":"Add current map","vac.route_delete":"Remove map","vac.route_delete_title":"Remove the map from its floor?","vac.route_delete_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_delete_confirm":"Remove","vac.route_space_title":"Move the map to another space?","vac.route_space_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_space_confirm":"Move","vac.route_missing_space_hint":"One of the maps points at a deleted space: pick a new one or remove the map. Until then the robot is not drawn for that map.","vac.route_default_hint":"The source reports no stable map id. Several floors cannot be told apart without another source or the robot's own selected map.","vac.route_target_hint":"The dock stays in the “{space}” space regardless of the current map.","vac.route_add_source":"Add a map source","vac.route_add_source_hint":"For integrations that give each map its own camera. The “All cameras” section above widens this list.","vac.route_source_no_map":"no map reported"};const wo={en:$o,ru:{"gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.show_room_tooltip":"Показывать окно с информацией при наведении на комнату","support.about_group":"О карточке","support.guide_group":"Документация","support.guide":"Руководство пользователя","support.form_group":"Отправить репорт или предложение","support.contact":"Контакт для связи (email/tg/WhatsApp), необязательно.","support.message":"Сообщение","support.attach":"Прикрепить обезличенную информацию из вашего плана","support.attach_hint":"В пакет входят только разрешённые геометрия, настройки и ограниченные сведения о среде. Не входят имена, HA id, текущие состояния, URL, файлы, сообщение и контакт.","support.geometry_warning":"Пакет не содержит имён и HA id, но содержит точную геометрию и размеры дома.","support.close_busy_title":"Прервать текущую операцию?","support.close_busy_body":"House Plan подготавливает или отправляет репорт. При закрытии ожидание результата прекратится.","support.building":"Подготавливаем обезличенные данные…","support.preview_summary":"Пакет v{version} · пространств: {spaces} · {size} КиБ","support.prepared":"Снимок подготовлен {n} мин назад","support.show_data":"Показать данные","support.download":"Скачать JSON","support.refresh":"Обновить снимок","support.privacy":"При отправке сообщение, необязательный контакт и, если выбран пакет, точная геометрия покидают ваш Home Assistant через relay проекта House Plan и доступны мейнтейнерам. Узел проекта хранит репорт до 30 дней. Сетевая инфраструктура видит адрес сервера HA, но House Plan не сохраняет его в исходном виде; метаданные лимитов и идемпотентности хранятся до 24 часов.","support.error_title":"Репорт не отправлен","support.manual_recovery":"Сообщение и вложение можно сохранить и продолжить вручную:","support.copy_message":"Копировать сообщение","support.success":"Репорт отправлен: {id}. Сохраните номер для связи с поддержкой.","support.copy_id":"Копировать ID","support.sending":"Отправляем…","support.retry":"Повторить","support.send":"Отправить","support.update_required":"Обновите карточку и интеграцию House Plan до совместимых версий, чтобы отправлять репорты.","support.message_copied":"Сообщение скопировано","support.id_copied":"ID репорта скопирован","support.copy_failed":"Не удалось получить доступ к буферу обмена. Выделите и скопируйте текст вручную.","support.validation.message_required":"Введите сообщение.","support.validation.message_too_long":"Сообщение должно содержать не более 10 000 символов.","support.validation.contact_too_long":"Контакт должен содержать не более 320 символов.","support.validation.preview_missing":"Дождитесь предпросмотра вложения или отключите его.","support.validation.preview_expired":"Предпросмотр вложения истёк. Обновите снимок.","support.error.support_invalid_message":"Проверьте сообщение и повторите попытку.","support.error.support_preview_expired":"Предпросмотр вложения истёк или больше недоступен. Обновите снимок.","support.error.support_package_too_large":"Обезличенный пакет превышает лимит 8 МиБ.","support.error.support_rate_limited":"Запрошено слишком много репортов или предпросмотров. Повторите позже.","support.error.support_unavailable":"Закрытая служба поддержки временно недоступна. Черновик сохранён в этом окне.","support.error.support_rejected":"Репорт не прошёл проверку службы поддержки.","support.error.unauthorized":"У вас нет прав на отправку этого репорта.","vac.routes_section":"Карты и этажи","vac.routes_current_map":"Текущая карта","vac.routes_no_map":"источник молчит","vac.routes_status":"Маршрутизация","vac.routes_state_ready":"карта сопоставлена и откалибрована","vac.routes_state_needs_calibration":"карта сопоставлена, нужна калибровка","vac.routes_state_unmapped":"текущая карта не сопоставлена этажу","vac.routes_state_ambiguous":"две карты неразличимы — этаж не выбирается","vac.routes_state_missing_space":"пространство карты удалено","vac.routes_state_none":"нет данных о карте","vac.routes_empty":"Ни одна карта ещё не сопоставлена этажу.","vac.route_map_default":"без идентификатора","vac.route_active":"Текущая","vac.route_space":"Пространство","vac.route_space_none":"не выбрано","vac.route_status_ready":"готова","vac.route_status_needs_calibration":"нужна калибровка","vac.route_status_ambiguous":"неоднозначна","vac.route_status_missing_space":"пространство удалено","vac.route_missing_space_group":"Пространство удалено","vac.route_add_current":"Добавить текущую карту","vac.route_delete":"Удалить карту","vac.route_delete_title":"Удалить карту с этажа?","vac.route_delete_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_delete_confirm":"Удалить","vac.route_space_title":"Перенести карту в другое пространство?","vac.route_space_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_space_confirm":"Перенести","vac.route_missing_space_hint":"Одна из карт ссылается на удалённое пространство: выберите новое или удалите карту. Пока ссылка не исправлена, робот на этой карте не рисуется.","vac.route_default_hint":"Источник не сообщает стабильный идентификатор карты. Несколько этажей нельзя различить без другого источника или собственной карты робота.","vac.route_target_hint":"Док остаётся в пространстве «{space}» независимо от текущей карты.","vac.route_add_source":"Добавить источник карты","vac.route_add_source_hint":"Для интеграций, где у каждой карты своя камера. Список расширяется разделом «Все камеры» выше.","vac.route_source_no_map":"карта не читается"},de:{"gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.show_room_tooltip":"Rauminformationen beim Darüberfahren anzeigen","support.about_group":"Über die Karte","support.guide_group":"Dokumentation","support.guide":"Benutzerhandbuch","support.form_group":"Bericht oder Vorschlag senden","support.contact":"Kontakt (E-Mail/tg/WhatsApp), optional.","support.message":"Nachricht","support.attach":"Anonymisierte Informationen aus dem Plan anhängen","support.attach_hint":"Enthält nur erlaubte Plangeometrie, Einstellungen und begrenzte Laufzeitdaten. Namen, Home-Assistant-IDs, Live-Zustände, URLs, Dateien, Nachricht und Kontakt werden ausgeschlossen.","support.geometry_warning":"Das Paket enthält keine Namen oder Home-Assistant-IDs, aber die exakte Geometrie und die Maße Ihres Hauses.","support.close_busy_title":"Aktuellen Vorgang abbrechen?","support.close_busy_body":"House Plan bereitet den Bericht vor oder sendet ihn. Beim Schließen wird nicht weiter auf das Ergebnis gewartet.","support.building":"Anonymisierte Daten werden vorbereitet…","support.preview_summary":"Paket v{version} · {spaces} Bereiche · {size} KiB","support.prepared":"Schnappschuss vor {n} Min. erstellt","support.show_data":"Daten anzeigen","support.download":"JSON herunterladen","support.refresh":"Schnappschuss aktualisieren","support.privacy":"Beim Senden verlassen Ihre Nachricht, der optionale Kontakt und, falls ausgewählt, die exakte Geometrie Ihren Home Assistant über das House-Plan-Projektrelay und sind für die Maintainer sichtbar. Der Projektserver speichert den Bericht bis zu 30 Tage. Die Netzwerkinfrastruktur sieht die Adresse Ihres HA-Servers, House Plan speichert sie jedoch nicht im Rohformat; Limit- und Idempotenzdaten bleiben bis zu 24 Stunden erhalten.","support.error_title":"Der Bericht wurde nicht gesendet","support.manual_recovery":"Nachricht und Anhang können gespeichert und manuell weitergegeben werden:","support.copy_message":"Nachricht kopieren","support.success":"Bericht gesendet: {id}. Bewahren Sie diese Nummer für den Support auf.","support.copy_id":"ID kopieren","support.sending":"Wird gesendet…","support.retry":"Erneut versuchen","support.send":"Senden","support.update_required":"Aktualisieren Sie House-Plan-Karte und Integration auf kompatible Versionen, um Berichte zu senden.","support.message_copied":"Nachricht kopiert","support.id_copied":"Berichts-ID kopiert","support.copy_failed":"Kein Zugriff auf die Zwischenablage. Markieren und kopieren Sie den Text manuell.","support.validation.message_required":"Geben Sie eine Nachricht ein.","support.validation.message_too_long":"Die Nachricht darf höchstens 10.000 Zeichen enthalten.","support.validation.contact_too_long":"Der Kontakt darf höchstens 320 Zeichen enthalten.","support.validation.preview_missing":"Warten Sie auf die Vorschau oder deaktivieren Sie den Anhang.","support.validation.preview_expired":"Die Anhangsvorschau ist abgelaufen. Aktualisieren Sie den Schnappschuss.","support.error.support_invalid_message":"Prüfen Sie die Nachricht und versuchen Sie es erneut.","support.error.support_preview_expired":"Die Anhangsvorschau ist abgelaufen oder nicht mehr verfügbar. Aktualisieren Sie den Schnappschuss.","support.error.support_package_too_large":"Das anonymisierte Paket überschreitet das Limit von 8 MiB.","support.error.support_rate_limited":"Zu viele Berichte oder Vorschauen wurden angefordert. Versuchen Sie es später erneut.","support.error.support_unavailable":"Der private Supportdienst ist vorübergehend nicht verfügbar. Der Entwurf bleibt in diesem Fenster erhalten.","support.error.support_rejected":"Der Bericht hat die Prüfung des Supportdienstes nicht bestanden.","support.error.unauthorized":"Sie dürfen diesen Bericht nicht senden.","vac.routes_section":"Karten und Etagen","vac.routes_current_map":"Aktuelle Karte","vac.routes_no_map":"keine Telemetrie","vac.routes_status":"Zuordnung","vac.routes_state_ready":"Karte zugeordnet und kalibriert","vac.routes_state_needs_calibration":"Karte zugeordnet, Kalibrierung nötig","vac.routes_state_unmapped":"aktuelle Karte keiner Etage zugeordnet","vac.routes_state_ambiguous":"zwei Karten nicht unterscheidbar — keine Etage gewählt","vac.routes_state_missing_space":"der Raum der Karte wurde gelöscht","vac.routes_state_none":"keine Kartendaten","vac.routes_empty":"Noch keine Karte einer Etage zugeordnet.","vac.route_map_default":"ohne Karten-ID","vac.route_active":"Aktiv","vac.route_space":"Raum","vac.route_space_none":"nicht gewählt","vac.route_status_ready":"bereit","vac.route_status_needs_calibration":"Kalibrierung nötig","vac.route_status_ambiguous":"mehrdeutig","vac.route_status_missing_space":"Raum gelöscht","vac.route_missing_space_group":"Gelöschter Bereich","vac.route_add_current":"Aktuelle Karte hinzufügen","vac.route_delete":"Karte entfernen","vac.route_delete_title":"Karte von der Etage entfernen?","vac.route_delete_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_delete_confirm":"Entfernen","vac.route_space_title":"Karte in einen anderen Raum verschieben?","vac.route_space_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_space_confirm":"Verschieben","vac.route_missing_space_hint":"Eine Karte verweist auf einen gelöschten Raum: neuen wählen oder Karte entfernen. Bis dahin wird der Roboter für diese Karte nicht gezeichnet.","vac.route_default_hint":"Die Quelle meldet keine stabile Karten-ID. Mehrere Etagen lassen sich ohne andere Quelle oder die vom Roboter gewählte Karte nicht unterscheiden.","vac.route_target_hint":"Die Station bleibt unabhängig von der aktuellen Karte im Raum „{space}“.","vac.route_add_source":"Kartenquelle hinzufügen","vac.route_add_source_hint":"Für Integrationen, die jeder Karte eine eigene Kamera geben. Der Abschnitt „Alle Kameras“ oben erweitert diese Liste.","vac.route_source_no_map":"keine Karte gemeldet"},fr:{"gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.show_room_tooltip":"Afficher les informations de la pièce au survol","support.about_group":"À propos de la carte","support.guide_group":"Documentation d’aide","support.guide":"Guide utilisateur","support.form_group":"Envoyer un rapport ou une suggestion","support.contact":"Contact (e-mail/tg/WhatsApp), facultatif.","support.message":"Votre message","support.attach":"Joindre des informations anonymisées de votre plan","support.attach_hint":"Inclut uniquement la géométrie, les réglages autorisés et des données d’exécution limitées. Exclut les noms, identifiants Home Assistant, états en direct, URL, fichiers, message et contact.","support.geometry_warning":"Le paquet ne contient ni noms ni identifiants Home Assistant, mais il contient la géométrie et les dimensions exactes de votre logement.","support.close_busy_title":"Interrompre l’opération en cours ?","support.close_busy_body":"House Plan prépare ou envoie le rapport. Fermer maintenant arrête l’attente du résultat.","support.building":"Préparation des données anonymisées…","support.preview_summary":"Paquet v{version} · {spaces} espaces · {size} Kio","support.prepared":"Instantané préparé il y a {n} min","support.show_data":"Afficher les données","support.download":"Télécharger le JSON","support.refresh":"Actualiser l’instantané","support.privacy":"Lors de l’envoi, votre message, le contact facultatif et, si elle est sélectionnée, la géométrie exacte quittent votre Home Assistant via le relais du projet House Plan et sont visibles par les mainteneurs. Le serveur du projet conserve le rapport jusqu’à 30 jours. L’infrastructure réseau voit l’adresse de votre serveur HA, mais House Plan ne la conserve pas en clair ; les métadonnées de limitation et d’idempotence sont gardées jusqu’à 24 heures.","support.error_title":"Le rapport n’a pas été envoyé","support.manual_recovery":"Vous pouvez conserver le message et la pièce jointe puis continuer manuellement :","support.copy_message":"Copier le message","support.success":"Rapport envoyé : {id}. Conservez ce numéro pour contacter l’assistance.","support.copy_id":"Copier l’ID","support.sending":"Envoi…","support.retry":"Réessayer","support.send":"Envoyer","support.update_required":"Mettez à jour la carte et l’intégration House Plan vers des versions compatibles pour envoyer des rapports.","support.message_copied":"Message copié","support.id_copied":"ID du rapport copié","support.copy_failed":"Impossible d’accéder au presse-papiers. Sélectionnez et copiez le texte manuellement.","support.validation.message_required":"Saisissez un message.","support.validation.message_too_long":"Le message ne doit pas dépasser 10 000 caractères.","support.validation.contact_too_long":"Le contact ne doit pas dépasser 320 caractères.","support.validation.preview_missing":"Attendez l’aperçu de la pièce jointe ou désactivez-la.","support.validation.preview_expired":"L’aperçu de la pièce jointe a expiré. Actualisez l’instantané.","support.error.support_invalid_message":"Vérifiez le message et réessayez.","support.error.support_preview_expired":"L’aperçu de la pièce jointe a expiré ou n’est plus disponible. Actualisez l’instantané.","support.error.support_package_too_large":"Le paquet anonymisé dépasse la limite de 8 Mio.","support.error.support_rate_limited":"Trop de rapports ou d’aperçus ont été demandés. Réessayez plus tard.","support.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible. Le brouillon reste dans cette fenêtre.","support.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","support.error.unauthorized":"Vous n’êtes pas autorisé à envoyer ce rapport.","vac.routes_section":"Cartes et étages","vac.routes_current_map":"Carte actuelle","vac.routes_no_map":"aucune télémétrie","vac.routes_status":"Routage","vac.routes_state_ready":"carte assignée et calibrée","vac.routes_state_needs_calibration":"carte assignée, calibrage requis","vac.routes_state_unmapped":"carte actuelle non assignée à un étage","vac.routes_state_ambiguous":"deux cartes indiscernables — aucun étage choisi","vac.routes_state_missing_space":"l'espace de la carte a été supprimé","vac.routes_state_none":"aucune donnée de carte","vac.routes_empty":"Aucune carte n'est encore assignée à un étage.","vac.route_map_default":"sans identifiant","vac.route_active":"Active","vac.route_space":"Espace","vac.route_space_none":"non sélectionné","vac.route_status_ready":"prête","vac.route_status_needs_calibration":"calibrage requis","vac.route_status_ambiguous":"ambiguë","vac.route_status_missing_space":"espace supprimé","vac.route_missing_space_group":"Espace supprimé","vac.route_add_current":"Ajouter la carte actuelle","vac.route_delete":"Supprimer la carte","vac.route_delete_title":"Retirer la carte de son étage ?","vac.route_delete_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_delete_confirm":"Supprimer","vac.route_space_title":"Déplacer la carte vers un autre espace ?","vac.route_space_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_space_confirm":"Déplacer","vac.route_missing_space_hint":"Une carte pointe vers un espace supprimé : choisissez-en un autre ou supprimez la carte. D'ici là, le robot n'est pas dessiné pour cette carte.","vac.route_default_hint":"La source ne fournit pas d'identifiant de carte stable. Plusieurs étages ne peuvent pas être distingués sans une autre source ou la carte sélectionnée du robot.","vac.route_target_hint":"La station reste dans l'espace « {space} » quelle que soit la carte actuelle.","vac.route_add_source":"Ajouter une source de carte","vac.route_add_source_hint":"Pour les intégrations qui donnent à chaque carte sa propre caméra. La section « Toutes les caméras » ci-dessus élargit cette liste.","vac.route_source_no_map":"aucune carte signalée"}};function ko(t,e,s){return i(wo[t]?.[e]??$o[e]??e,s)}function xo(t,e,s,i,o){return{markerId:t,source:i,mapId:o,space:e.length?"":s}}function So(t,e,s){return{...t,space:s.has(e)?e:""}}function Mo(t,e=Math.random){const s=new Set(t);for(let t=0;t<64;t+=1){const t="vr_"+Math.floor(16777215*e()).toString(36).padStart(5,"0");if(!s.has(t))return t}return"vr_"+Date.now().toString(36)}function Do(t,e,s){return t.map(t=>t.id===e?{...t,calibration:s}:t)}const Co=new WeakMap,Po=(t,e)=>{const s=(t._serverCfg?.spaces||[]).find(t=>t?.id===e);return String(s?.name||s?.id||e)};function Io(t,e,s){const i=t.host,n=l(i.hass,i._config?.language),r=(t,e)=>ko(n,t,e),g=e.marker?.vacuum||{},f=Array.isArray(g.map_routes),b=i._vacSource(e)||"",v=o(e.id,g,e.space,b),y=(i._serverCfg?.spaces||[]).filter(t=>"string"==typeof t?.id&&!!t.id).map(t=>({id:t.id,name:String(t.name||t.id)})),$=new Set(y.map(t=>t.id)),w=h(v,[b],t=>i._vacObservedMapId(e,t)),k=c({routes:v,observed:w,spaceIds:$}),x="ready"===k.kind||"needs_calibration"===k.kind||"missing_space"===k.kind?k.route.id:"",S=b?w[b]:void 0,M=async s=>{let o=f?g.map_routes??null:null;if(!o&&(o=function(t,e,s,i){if(!s)return null;const o=t?.calibration;if(!o||"object"!=typeof o)return null;const n=new Set,r=[];for(const t of Object.keys(o)){const l=a(o[t]);if(!l)continue;const h=i(n);n.add(h),r.push({id:h,source:s,map_id:t,space:e,calibration:l})}return r.length?r:null}(g,e.space,b,t=>Mo(t))??[],!b&&Object.keys(g.calibration||{}).length))return!1;const n=s(o);return!!n&&(async s=>{const o=i._serverCfg;if(!o)return!1;const a=JSON.parse(JSON.stringify(o));a.markers=a.markers||[];let n=a.markers.find(t=>t.id===e.id);if(!n){if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return!1;n={id:e.id,binding:`${e.bindingKind}:${e.bindingRef}`,space:e.space||null,area:e.area||null,hidden:!!e.hidden},a.markers.push(n)}n.vacuum={...n.vacuum||{},map_routes:s},delete n.vacuum.calibration;const r=u(o,a,i._cfgContentFingerprint,i._cfgRev,_);i._serverCfg=a,i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._saveConfigDebounced.pending()&&i._saveConfigDebounced.cancel();try{return await t._saveConfigNow(),!0}catch(t){return m(i,r,_),i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._showToast(i._t("toast.cfg_save_failed",{err:i._errText(t)})),!1}})(n)},D=s.candidates.filter(t=>t.entityId!==b&&!v.some(e=>e.source===t.entityId)),C=async(t,e)=>{if(!e||e===t.space)return;await i._confirmDanger({key:"vacuum_route_space",kind:"warning",title:r("vac.route_space_title"),message:r("vac.route_space_body"),objectName:t.map_id||r("vac.route_map_default"),confirmLabel:r("vac.route_space_confirm"),cancelLabel:i._t("btn.cancel")})&&await M(s=>function(t,e,s,i){return t.map(t=>t.id===e?{...t,id:i,space:s,calibration:null}:t)}(s,t.id,e,Mo(new Set(v.map(t=>t.id)))))},P=new Map(y.map((t,e)=>[t.id,e])),I=(t,e)=>t.map_id.localeCompare(e.map_id)||t.source.localeCompare(e.source)||t.id.localeCompare(e.id),T=v.filter(t=>$.has(t.space)).sort((t,e)=>(P.get(t.space)??0)-(P.get(e.space)??0)||I(t,e)),z=v.filter(t=>!$.has(t.space)).sort(I),R=[...T,...z],A=!!b&&void 0!==S&&!v.some(t=>t.source===b&&t.map_id===S);let F=Co.get(t);const N=F?.source||"",H=F?.mapId||"",L=!!F&&v.some(t=>t.source===N&&t.map_id===H);F&&(F.markerId!==e.id||i._vacObservedMapId(e,F.source)!==F.mapId||L&&!F.saving)&&(Co.delete(t),F=void 0),F?.space&&!$.has(F.space)&&(F=So(F,"",$),Co.set(t,F));const E=s=>p` +
  • +
    + ${s.map_id||r("vac.route_map_default")} + ${s.id===x?p`${r("vac.route_active")}`:d} +
    + ${s.source} +
    ${(t=>$.has(t.space)?t.calibration?"ambiguous"===k.kind&&k.routeIds.includes(t.id)?r("vac.route_status_ambiguous"):r("vac.route_status_ready"):r("vac.route_status_needs_calibration"):r("vac.route_status_missing_space"))(s)}
    + +
    + + +
    +
  • `,O=`vacroute-missing-${e.id.replace(/[^a-zA-Z0-9_-]/g,"-")}`;return p` + +
    +
    +
    ${r("vac.routes_current_map")}${void 0===S?r("vac.routes_no_map"):S||r("vac.route_map_default")}
    +
    ${r("vac.routes_status")}${r(`vac.routes_state_${k.kind}`)}
    +
    + ${T.length||F?p`
      + ${F?p` +
    • +
      ${F.mapId||r("vac.route_map_default")}
      + ${F.source} + +
      + + +
      +
    • `:d} + ${T.map(E)} +
    `:d} + ${z.length?p` +
    +

    + ${r("vac.route_missing_space_group")} +

    +
      ${z.map(E)}
    +
    `:d} + ${R.length||F?d:p`
    ${r("vac.routes_empty")}
    `} +
    + +
    + ${D.length?p`
    + ${r("vac.route_add_source")} +
    ${r("vac.route_add_source_hint")}
    +
    + ${D.map(s=>{const o=i._vacObservedMapId(e,s.entityId);return p``})} +
    +
    `:d} + ${R.some(t=>!$.has(t.space))?p` +
    ${r("vac.route_missing_space_hint")}
    `:d} + ${v.some(t=>"default"===t.map_id)?p` +
    ${r("vac.route_default_hint")}
    `:d} + ${R.length?p`
    ${r("vac.route_target_hint",{space:Po(i,e.space)})}
    `:d} +
    `}const To=new WeakMap;function zo(t,e,s){let i=To.get(t);i||(i=new Map,To.set(t,i));const o=i.get(e);if(o)return void(o.run=s);const a={run:s};i.set(e,a),queueMicrotask(()=>{i.get(e)===a&&(i.delete(e),a.run())})}function Ro(t,e){const s=To.get(t),i=s?.get(e);i&&(s.delete(e),i.run())}function Ao(t,e){const s=To.get(t);if(!s)return;const i=e?[...s].filter(([t])=>t===e):[...s];for(const[t]of i)s.delete(t);s.size||To.delete(t)}const Fo=new WeakMap,No=new Set(["_cursorPt","_opMeasure","_physicalDrag","_physicalRotate","_decorDraft","_dtBox","_dtDrag","_bdDrag","_furnPreviewInput","_layout"]),Ho=new Set(["_cursorPt","_opMeasure","_furnPreviewInput"]),Lo=new Set(["_physicalDrag","_physicalRotate","_decorDraft","_dtDrag","_bdDrag"]),Eo=t=>{let e=Fo.get(t);return e||(e={raf:0,hidden:[],transparent:[],dimmed:[]},Fo.set(t,e)),e},Oo=t=>!!("view"!==t._mode&&(t._deviceDrag||t._physicalDrag||t._physicalRotate||t._decorDraft||t._decorMove||t._dtDrag||t._bdDrag||t._opDrag||t._resize?.dragging));function Bo(t,e,s){const i=t;if(!i?.isConnected||"view"===i._mode)return!1;const o=void 0!==e&&No.has(e);if("_layout"===e&&!i._deviceDrag)return!1;if("_dtBox"===e&&!i._dtDrag)return!1;if(void 0!==e&&Lo.has(e)&&null==s&&null!=i[e]&&Oo(i))return!1;if(void 0!==e&&Lo.has(e)&&null==i[e])return!1;return!!(o&&(Ho.has(e)||Oo(i))||void 0===e&&Oo(i))&&(function(t){const e=Eo(t);if(e.raf||"function"!=typeof requestAnimationFrame)return;e.raf=requestAnimationFrame(()=>{e.raf=0,function(t){const e=t,s=e.renderRoot;if(!s)return;const i=Eo(e);Go(i),Ko(e,s);const o=s.querySelector("[data-hp-live-editor]");if(!o)return;if("plan"===e._mode)jo(i,s,".hp-editor-only-layer:not(.hp-plan-snap-layer)"),e._opDrag&&Wo(i,s,".wallbodies"),e._resize?.dragging&&(Yo(i,s,".wallbodies"),jo(i,s,".resize-layer"),Jo(i,s,e._resize.plan?.movingOpeningIds||[])),e._opDrag&&jo(i,s,".openinglayer");else if("decor"===e._mode){const t=e._dtDrag?.id||e._decorMove?.id;t&&Vo(i,s,t),jo(i,s,".dtframe, .backdropframe")}g(Uo(e),o);const a=s.querySelector("[data-hp-live-editor-html]");if(a){const t=e._viewOr(e._baseVb());g(e._renderLiveEditorMeasurements(t),a)}e._liveEditorPaintCount=(e._liveEditorPaintCount||0)+1}(t)})}(i),!0)}const Wo=(t,e,s)=>{for(const i of e.querySelectorAll(s))i.closest("[data-hp-live-editor]")||"hidden"!==i.style.visibility&&(i.style.visibility="hidden",t.hidden.push(i))},jo=(t,e,s)=>{for(const i of e.querySelectorAll(s))i.closest("[data-hp-live-editor]")||"0"!==i.style.opacity&&(i.style.opacity="0",t.transparent.push(i))},Go=t=>{for(const e of t.hidden)e.style.removeProperty("visibility");t.hidden.length=0;for(const e of t.transparent)e.style.removeProperty("opacity");t.transparent.length=0;for(const e of t.dimmed)e.style.removeProperty("opacity");t.dimmed.length=0},qo=(t,e,s=[])=>{if("iso"===t._renderProjection)return f``;const i=t._spaceModel();if(!i)return f``;const o=new Set(s),a=(i.rooms||[]).filter(t=>!!t.id&&o.has(t.id)).flatMap(t=>{const e=t.poly||[];return e.map((t,s)=>[t,e[(s+1)%e.length]])}),n=(t,e)=>{const[s,i]=e,o=i[0]-s[0],a=i[1]-s[1],n=o*o+a*a;if(!(n>0))return!1;const r=((t[0]-s[0])*o+(t[1]-s[1])*a)/n,l=Math.max(0,Math.min(1,r));return Math.hypot(t[0]-s[0]-o*l,t[1]-s[1]-a*l)<1e-4},r=[...t._spaceWalls.filter(t=>{const e=(t.a||[]).map(t=>t*b),s=(t.b||[]).map(t=>t*b);return a.some(t=>n(e,t)&&n(s,t))}).map((e,s)=>((e,s,i,o)=>{const a=Math.hypot(s[0]-e[0],s[1]-e[1]);if(!(a>1e-9&&i>0))return null;const n=x(i,t._cellCm,t._gridPitch)/2,r=-(s[1]-e[1])/a*n,l=(s[0]-e[0])/a*n;return{key:o,kind:"outer",cm:i,a:e,b:s,depthUnits:2*n,quad:[[e[0]+r,e[1]+l],[s[0]+r,s[1]+l],[s[0]-r,s[1]-l],[e[0]-r,e[1]-l]]}})((e.a||[]).map(t=>t*b),(e.b||[]).map(t=>t*b),e.cm,e.key||`wall-${s}`))].filter(t=>!!t),l=t._openingsR.map(t=>({x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),h=r.map(t=>v(t,l)).filter(Boolean);if(!h.length)return f``;const c=Math.max(...r.map(t=>t.depthUnits),0),d=t._viewOr(t._baseVb()),p=t._stageEl?.clientWidth&&d.w?t._stageEl.clientWidth/d.w:1,u=y(c,p)||$(w(t._cellCm),p),_=e?.color||"#607d8b",m=t._fillColors.wall_fill,g=h.join(" ");return f` + + + `},Uo=t=>{if("plan"===t._mode)return(t=>{const e=t._baseVb(),s=t._viewOr(e),i=t._opMeasureView;if(t._resize?.dragging){const e=t._spaceDisplayForRender(),i=t._resize.plan;return f``}const o=t._spaceDisplayForRender();return f``})(t);if("decor"===t._mode){const e=t._viewOr(t._baseVb()),s=t._dtDrag?.id||t._decorMove?.id||null;return f``}return d},Ko=(t,e)=>{const s=t._deviceDrag;if(!s)return;const i=t._devices.find(t=>t.id===s.id),o=[...e.querySelectorAll('[data-hp="device"]')].find(t=>t.dataset.id===s.id);if(!i||!o)return;const a=t._livePos(i),n=t._scenePoint([a.x,a.y]),r=t._viewOr(t._baseVb());o.style.left=(n[0]-r.x)/r.w*100+"%",o.style.top=(n[1]-r.y)/r.h*100+"%"},Vo=(t,e,s)=>{for(const i of e.querySelectorAll('.decorlayer [data-hp="decor"]'))i.closest("[data-hp-live-editor]")||i.dataset.id!==s||"0"!==i.style.opacity&&(i.style.opacity="0",t.transparent.push(i))},Jo=(t,e,s)=>{const i=new Set(s);for(const s of e.querySelectorAll('.openinglayer [data-hp="opening"]'))!s.closest("[data-hp-live-editor]")&&i.has(s.dataset.id||"")&&(s.style.opacity="0",t.transparent.push(s))},Yo=(t,e,s)=>{for(const i of e.querySelectorAll(s))i.closest("[data-hp-live-editor]")||(i.style.opacity="0.35",t.dimmed.push(i))};const Xo=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Zo(t){return Number.isFinite(t)?(t%360+360)%360:0}function Qo(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function ta(t){const e=Qo(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function ea(t){const e=Xo(t.r,0,255)/255,s=Xo(t.g,0,255)/255,i=Xo(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Zo(n),s:0===o?0:a/o*100,v:100*o}}function sa(t){return function(t){const e=t=>Math.round(Xo(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Zo(t.h),s=Xo(t.s,0,100)/100,i=Xo(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}const ia={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class oa extends M{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=ia,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new io(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Qo(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=oa.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=oo(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=ao(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||ia;return{title:t.title||ia.title,hue:t.hue||ia.hue,saturation:t.saturation||ia.saturation,value:t.value||ia.value,hex:t.hex||ia.hex,invalidHex:t.invalidHex||ia.invalidHex}}_syncFromColor(t,e){const s=Qo(D(t,"#607d8b"))||"#607d8b",i=ta(s);if(!i)return;const o=ea(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(sa({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Qo(e),i=s?ta(s):null;if(!s||!i)return;const o=ea(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Qo(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Qo(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=sa({h:this._hue,s:this._saturation,v:this._value}),i=sa({h:this._hue,s:100,v:100}),o=this._labels(),a=Math.round(this._hue),n=Math.round(this._saturation),r=Math.round(this._value);return p` + `}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=D(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return p` + ${this.label?p`${this.label}`:d} + + ${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):d} + `}}oa.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},oa.styles=S` + :host { + position: relative; + display: inline-flex; + align-items: center; + gap: 6px; + min-width: 0; + } + .label { + font-size: 12px; + color: var(--secondary-text-color, #9aa4ad); + white-space: nowrap; + } + .trigger { + width: 40px; + height: 40px; + flex: none; + box-sizing: border-box; + padding: 3px; + border: 1px solid var(--divider-color, #666); + border-radius: 7px; + background: + linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, + linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; + cursor: pointer; + } + :host([data-pointer-hover]) .trigger:hover { + border-color: var(--primary-color, #03a9f4); + } + .trigger[aria-expanded='true'] { + border-color: var(--primary-color, #03a9f4); + outline: none; + box-shadow: 0 0 0 1px var(--primary-color, #03a9f4); + } + .trigger:focus-visible { + border-color: var(--primary-color, #03a9f4); + outline: 2px solid var(--primary-color, #03a9f4); + outline-offset: 2px; + } + .swatch { + display: block; + width: 100%; + height: 100%; + border-radius: 3px; + box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.18); + pointer-events: none; + } + .picker { + /* The popover attribute promotes this surface into the browser top layer, outside + hp-dialog's scrolling/clipping body. JS supplies viewport coordinates. */ + position: fixed; + z-index: 2147483647; + inset: auto; + top: 0; + left: 0; + width: min(292px, calc(100vw - 16px)); + max-height: calc(100vh - 16px); + margin: 0; + box-sizing: border-box; + display: grid; + gap: 10px; + padding: 12px; + overflow: auto; + color: var(--primary-text-color, #fff); + background: var(--card-background-color, #202126); + border: 1px solid var(--primary-color, #03a9f4); + border-radius: 10px; + box-shadow: 0 10px 28px rgb(0 0 0 / 0.34); + } + .picker[popover]:not(:popover-open) { + /* Keep the author-level display:grid from exposing the surface for one + frame before showPopover() promotes it into the top layer. */ + display: none; + } + .picker-head, + .row, + .control-head { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + } + .picker-head { + justify-content: space-between; + } + .caption { + flex: 1; + min-width: 0; + overflow: hidden; + color: var(--secondary-text-color, #9aa4ad); + font-size: 12px; + text-overflow: ellipsis; + white-space: nowrap; + } + .preview { + width: 52px; + height: 30px; + flex: none; + box-sizing: border-box; + padding: 3px; + border: 1px solid var(--divider-color, #666); + border-radius: 7px; + background: + linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, + linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; + } + .preview > span { + display: block; + width: 100%; + height: 100%; + border-radius: 3px; + } + .sv-field { + position: relative; + width: 100%; + height: 132px; + overflow: hidden; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 8px; + background: + linear-gradient(to top, #000, transparent), + linear-gradient(to right, #fff, var(--hp-picker-hue, #f00)); + cursor: crosshair; + touch-action: none; + user-select: none; + } + .sv-thumb { + position: absolute; + left: var(--hp-picker-saturation, 0%); + top: var(--hp-picker-value, 100%); + width: 14px; + height: 14px; + box-sizing: border-box; + border: 2px solid #fff; + border-radius: 50%; + box-shadow: 0 0 0 1px rgb(0 0 0 / .65), 0 1px 3px rgb(0 0 0 / .5); + transform: translate(-50%, -50%); + pointer-events: none; + } + .control { + display: grid; + gap: 2px; + min-width: 0; + } + .control-value { + flex: none; + color: var(--secondary-text-color, #9aa4ad); + font-size: 12px; + } + input[type='range'] { + width: auto; + height: 40px; + min-width: 0; + flex: 1; + margin: -5px 0; + accent-color: var(--primary-color, #03a9f4); + touch-action: none; + } + .hue-range { + accent-color: var(--hp-picker-hue, #f00); + --hp-picker-hue-track: linear-gradient(to right, + #f00 0%, #ff0 16.667%, #0f0 33.333%, #0ff 50%, + #00f 66.667%, #f0f 83.333%, #f00 100%); + --hp-picker-hue-thumb-shadow: + 0 0 0 2px var(--card-background-color, #202126), + 0 0 0 3px var(--primary-text-color, #fff); + } + .hue-range::-webkit-slider-runnable-track { + height: 10px; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 999px; + background: var(--hp-picker-hue-track); + } + .hue-range::-moz-range-track { + height: 10px; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 999px; + background: var(--hp-picker-hue-track); + } + .hue-range::-moz-range-progress { + height: 10px; + border: 0; + border-radius: 999px; + background: transparent; + } + .hue-range::-webkit-slider-thumb { + border-radius: 50%; + box-shadow: var(--hp-picker-hue-thumb-shadow); + } + .hue-range::-moz-range-thumb { + border-radius: 50%; + box-shadow: var(--hp-picker-hue-thumb-shadow); + } + input[type='text'], + input[type='number'] { + min-height: 40px; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 6px; + padding: 6px 8px; + color: var(--primary-text-color, #fff); + background: var(--input-fill-color, transparent); + font: inherit; + } + input[type='text'] { + width: 100%; + } + input[type='number'] { + width: 50px; + flex: none; + } + input:focus-visible, + .sv-field:focus-visible { + outline: 2px solid var(--primary-color, #03a9f4); + outline-offset: 1px; + } + input[aria-invalid='true'] { + border-color: var(--error-color, #db4437); + } + .error { + color: var(--error-color, #db4437); + font-size: 11px; + line-height: 1.25; + } + .pct { + color: var(--secondary-text-color, #9aa4ad); + font-size: 12px; + } + :host([disabled]) { + opacity: .5; + pointer-events: none; + } + @media (prefers-reduced-motion: reduce) { + .picker { transition: none !important; } + } + @media (forced-colors: active) { + .hue-range::-webkit-slider-runnable-track { + border-color: ButtonText; + background: Canvas; + } + .hue-range::-moz-range-track { + border-color: ButtonText; + background: Canvas; + } + .hue-range::-webkit-slider-thumb { + box-shadow: none; + } + .hue-range::-moz-range-thumb { + box-shadow: none; + } + } + `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",oa);const aa=new WeakMap;function na(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=aa.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},aa.set(e,s)),s}function ra(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function la(t,e){const s=na(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,ra(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,ra(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,ra(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function ha(t){const e=na(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function ca(t,e,s=ha(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function da(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:ca(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function pa(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function ua(t,e,s,i=ha(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return da(t,o.platform||n,"entity-platform",i,a)}class _a extends M{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=la(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return l(this.hass)}_t(t,e){return P(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=ha(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=ua(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return da(t,s?.domain,"registry-owner",i,e)});if(l.some(Boolean))return pa(l);const h=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&h.push(ua(t,e,s,i));return h.some(Boolean)?pa(h):pa((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return da(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,ha(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=ua(this.hass,t,this.registry,ha(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return d;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=I(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),o="none"!==t.pulse.kind,a=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let n=-a/2,r=a/2,l=-a/2,h=a/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(r=Math.max(r,i+t)),"left"===s.valueBadge.position&&(n=Math.min(n,-i-t)),"top"===s.valueBadge.position&&(l=Math.min(l,-i-e)),"bottom"===s.valueBadge.position&&(h=Math.max(h,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);h=Math.max(h,t)}const c=Math.min(1,2.35/Math.max(1,r-n),2.35/Math.max(1,h-l)),u=Math.round(100*c),_=[`left:calc(50% - ${54*((n+r)/2)*c}px)`,`top:calc(50% - ${54*((l+h)/2)*c}px)`,c<1?`transform:scale(${c})`:""].filter(Boolean).join(";"),m=["dev",T(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),g=z(s).join(";"),f=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),b=[this.deviceName,this._providerSummary(i),this._stateSummary(t),f,t.valueFullText||"",R(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return p`
    +
    + ${this._t("marker.preview.title")} + + ${this._t(e?"marker.preview.example":"marker.preview.actual")} + +
    +
    + +
    +
    ${this._t("marker.preview.integration")}${this._providerSummary(i)}
    +
    ${this._t("marker.preview.source")}${this._sourceSummary(t)}
    +
    ${this._t("marker.preview.current_state")}${this._stateSummary(t)}
    +
    ${this._t("marker.preview.result")}${f}
    + ${t.fallbackReason?p`

    ${this._reason(t.fallbackReason)}

    `:d} + ${t.explanation.notices.map(t=>p`

    ${this._reason(t)}

    `)} + ${c<1?p`

    ${this._t("marker.preview.scaled",{n:u})}

    `:d} + ${e&&this._reducedMotion?p`

    ${this._t("marker.preview.reduced_motion")}

    `:d} +
    +
    + ${t.visualSources.length||t.criticalSources.length?p`
    + ${this._t("marker.preview.details")} +
    + ${[...t.visualSources,...t.criticalSources].map(t=>p` +
    + ${t.name}${t.eid} + ${t.stateText||t.state} + ${this._sourceProvider(t.eid)} +
    `)} +
    +
    `:d} + ${"icon_ripple"===t.display?p`
    + + +
    `:d} +
    `}}_a.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},_a.styles=[C,S` + :host { display: block; min-width: 0; } + .devicepreview { + margin: 4px 0 14px; + padding: 14px; + border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.28)); + border-radius: 14px; + background: color-mix(in srgb, var(--card-background-color, #20232b) 92%, var(--primary-color, #03a9f4)); + color: var(--primary-text-color, #fff); + overflow: hidden; + } + .previewhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; } + .previewbadge { + padding: 3px 9px; + border-radius: 999px; + color: var(--secondary-text-color, #9aa0aa); + background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 12%, transparent); + font-size: 12px; + font-weight: 700; + } + .previewbadge.example { color: var(--warning-color, #ffb300); } + .previewgrid { display: grid; grid-template-columns: minmax(170px, 0.8fr) minmax(240px, 1.2fr); gap: 16px; align-items: stretch; } + .previewstage { + position: relative; + min-height: 168px; + border-radius: 12px; + background: var(--secondary-background-color, rgba(0, 0, 0, 0.16)); + overflow: hidden; + pointer-events: none; + container-type: inline-size; + --icon-size: 54px; + --device-base-size: 48.6px; + } + .previewfit { position: absolute; left: 50%; top: 50%; transform-origin: center; } + .previewstage .dev { left: 0; top: 0; cursor: default; } + :host([data-pointer-hover]) .previewstage .dev:hover { z-index: 2; } + .previewfacts { min-width: 0; display: grid; align-content: start; gap: 8px; } + .previewfacts > div { display: grid; grid-template-columns: minmax(115px, 0.8fr) minmax(0, 1.3fr); gap: 8px; } + .previewfacts span { color: var(--secondary-text-color, #9aa0aa); } + .previewfacts b { min-width: 0; overflow-wrap: anywhere; } + .previewnotice { margin: 2px 0 0; color: var(--secondary-text-color, #9aa0aa); font-size: 13px; } + .previewdetails { margin-top: 12px; } + .previewdetails summary { cursor: pointer; color: var(--secondary-text-color, #9aa0aa); } + .previewtable { display: grid; gap: 8px; margin-top: 9px; } + .previewsource { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3px 10px; font-size: 13px; } + .previewsource > * { min-width: 0; overflow-wrap: anywhere; } + .previewsource code { color: var(--secondary-text-color, #9aa0aa); } + .previewdemo { + min-height: 40px; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.35)); + border-radius: 10px; + background: transparent; + color: inherit; + cursor: pointer; + } + .previewdemos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } + .previewdemo:focus-visible { outline: 2px solid var(--primary-color, #03a9f4); outline-offset: 2px; } + .previewdemo:disabled { opacity: 0.5; cursor: not-allowed; } + @media (max-width: 640px) { + .previewgrid { grid-template-columns: minmax(0, 1fr); } + .previewstage { min-height: 142px; } + .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } + } + `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",_a);const ma={revision:0,topologies:[],states:{}};class ga extends M{constructor(){super(...arguments),this.value={enabled:!1,z2mBaseTopics:[]},this.savedEnabled=!1,this.devices=[],this._runtime=null,this._snapshot=ma,this._topicText="zigbee2mqtt",this._invalidTopic=!1}disconnectedCallback(){this._release?.(),this._release=void 0,super.disconnectedCallback()}updated(t){t.has("hass")&&(this._release?.(),this._release=void 0,this._runtime=null,this._snapshot=ma),this.savedEnabled&&this._admin&&this._ensureRuntime()}willUpdate(t){t.has("value")&&(this._topicText=(this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]).join("\n"),this._invalidTopic=!1)}get _admin(){return!0===this.hass?.user?.is_admin}_t(t,e){return go(l(this.hass),t,e)}async _ensureRuntime(){return this._runtime||(this._runtime=await import("./zigbee-topology-runtime-DUxfg4Z9.js"),this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this._release=this._runtime.subscribeZigbeeTopology(this.hass,()=>{this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this.requestUpdate()}),this.requestUpdate()),this._runtime}_emit(t){this._admin&&this.dispatchEvent(new CustomEvent("hp-topology-settings-change",{detail:t,bubbles:!0,composed:!0}))}_topics(){return this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]}_editTopics(t){this._topicText=t;const e=t.split(/[\r\n,]+/).map(t=>t.trim()).filter(Boolean),s=e.map(F).filter(Boolean);this._invalidTopic=s.length!==e.length,this._invalidTopic?this.requestUpdate():this._emit({...this.value,z2mBaseTopics:[...new Set(s)].slice(0,8)})}_status(t){const e=this._snapshot.states[t];if(!e)return this._t("status_idle");if("loading"===e.phase)return this._t("status_loading");if("error"===e.phase)return this._t(`error_${e.error||"provider"}`);if("ready"!==e.phase||!e.obtainedAt)return this._t("status_idle");const s=new Date(e.obtainedAt).toLocaleTimeString(l(this.hass),{hour:"2-digit",minute:"2-digit"}),i=this._snapshot.topologies.find(e=>"zha"===t?"zha"===e.provider:`z2m:${e.instanceId}`===t),o=!!i&&!!this.registry&&fo(i,this.devices,this.registry).warnings.length>0;return e.partial||o?this._t("status_partial",{time:s}):i&&!i.links.length?this._t("status_no_links",{time:s}):this._t(Date.now()-e.obtainedAt>bo?"status_stale":"status_ready",{time:s})}_busy(t){return"loading"===this._snapshot.states[t]?.phase}async _readZha(){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).readZhaTopology(this.hass)}async _refreshZ2m(t){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).refreshZ2mTopology(this.hass,t)}render(){const t=this._admin,e=this.value.enabled,s=t&&this.savedEnabled,i=this._topics();return p` +
    ${this._t("title")}
    + +
    ${this._t("hint")}
    + ${t?d:p`
    ${this._t("admin_only")}
    `} + ${e?p`
    + ${this.savedEnabled?d:p`
    ${this._t("save_first")}
    `} +
    +
    ${this._t("zha")}
    +
    ${this._t("zha_hint")}
    +
    + + ${this._status("zha")} +
    +
    +
    +
    ${this._t("z2m")}
    + + + ${this._invalidTopic?p`
    ${this._t("error_invalid_topic")}
    `:d} +
    ${this._t("z2m_warning")}
    + ${i.map(t=>p`
    + + ${this._status(`z2m:${t}`)} +
    `)} +
    +
    `:d} + `}}ga.properties={hass:{attribute:!1},value:{attribute:!1},savedEnabled:{type:Boolean,attribute:"saved-enabled"},devices:{attribute:!1},registry:{attribute:!1}},ga.styles=S` + :host { display: block; color: inherit; font: inherit; } + .section { margin-top: 18px; font-weight: 700; } + .toggle { display: flex; align-items: center; gap: 10px; margin-top: 10px; } + .hint, .status { color: var(--secondary-text-color, #9aa0aa); font-size: 12px; line-height: 1.45; } + .hint { margin-top: 6px; } + .providers { display: grid; gap: 14px; margin-top: 12px; padding-left: 34px; } + .provider { display: grid; gap: 7px; } + .provider-title { font-weight: 650; } + .actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } + textarea { + box-sizing: border-box; width: 100%; min-height: 58px; resize: vertical; + border: 1px solid var(--divider-color, #666); border-radius: 8px; padding: 8px; + color: var(--primary-text-color, inherit); background: var(--card-background-color, #202126); + font: inherit; + } + button { + display: inline-flex; align-items: center; gap: 6px; min-height: 36px; + border: 1px solid var(--divider-color, #666); border-radius: 9px; padding: 7px 11px; + color: inherit; background: transparent; cursor: pointer; font: inherit; font-weight: 600; + } + button:disabled, textarea:disabled { opacity: .5; cursor: default; } + .warning { color: var(--warning-color, #d89300); font-size: 12px; line-height: 1.45; } + `,customElements.get("hp-zigbee-topology-settings")||customElements.define("hp-zigbee-topology-settings",ga);const fa=(t,e)=>[t[0]-e[0],t[1]-e[1]],ba=(t,e)=>[t[0]+e[0],t[1]+e[1]],va=(t,e)=>t[0]*e[0]+t[1]*e[1],ya=t=>Math.hypot(t[0],t[1]);function $a(t){let e=0;for(let s=0;st[1]!=l>t[1]&&t[0]<(r-a)*(t[1]-n)/(l-n)+a&&(s=!s)}return s}([r[0]+n[0]*l,r[1]+n[1]*l],t)&&(n=[-n[0],-n[1]]),n}function xa(t){const e=t.length;if(e<3)return!1;for(let s=0;s(t[0]-o[0])*r[1]-(t[1]-o[1])*r[0],h=t=>(t[0]-o[0])*r[0]+(t[1]-o[1])*r[1],c=s,d=n-s;for(const e of t){const t=Math.abs(l(e));if(t<=s)continue;const o=h(e);o<=c||o>=d||tMath.abs(n+(t-p)/g*(r-n));i=Math.min(i,f(_),f(m))}}return i}function Ma(t,e,s){if(E(t,e,s))return!0;if(L(t,e,s)||L(e,t,s))return!1;let i=0;try{const s=O([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of s)t?.[0]&&(i+=B(t[0]))}catch{return!1}return i>Math.max(1e-7,s*s)}function Da(t,e,s){for(const i of e)for(let e=0;es)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-s&&h+t.length/2<=r+s)return!0}return!1}function Ca(t,e,s){const i=Math.abs(e[0]-t[0]),o=Math.abs(e[1]-t[1]);return i<=s&&o>s?"v":o<=s&&i>s?"h":null}function Pa(t,e,s){return Math.abs(t[0]-e[0])<=s&&Math.abs(t[1]-e[1])<=s}function Ia(t,e,s,i,o){return Pa(t,s,o)&&Pa(e,i,o)||Pa(t,i,o)&&Pa(e,s,o)}function Ta(t,e,s,i,o){const a=Ca(t,e,o);return a&&Ca(s,i,o)===a?"h"===a?Math.abs(t[1]-s[1])>o||Math.abs(t[1]-i[1])>o?0:Math.max(0,Math.min(Math.max(t[0],e[0]),Math.max(s[0],i[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s[0],i[0]))):Math.abs(t[0]-s[0])>o||Math.abs(t[0]-i[0])>o?0:Math.max(0,Math.min(Math.max(t[1],e[1]),Math.max(s[1],i[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s[1],i[1]))):0}function za(t,e,s,i){const o=t.length,a=Ca(t[(e-1+o)%o],t[e],i),n=Ca(t[(e+1)%o],t[(e+2)%o],i),r="h"===s?"v":"h";return a===r&&n===r}function Ra(t){let e=1/0,s=1/0,i=-1/0,o=-1/0;for(const a of t)e=Math.min(e,a[0]),s=Math.min(s,a[1]),i=Math.max(i,a[0]),o=Math.max(o,a[1]);return[e,s,i,o]}function Aa(t,e,s){const i=Ra(t),o=Ra(e);return i[2]o}const Na=(t,e)=>"h"===e?t[0]:t[1],Ha=(t,e)=>"h"===e?t[1]:t[0];function La(t,e,s,i,o){const a=t.find(t=>t.id===e);if(!a)return null;const n=a.poly[s],r=a.poly[(s+1)%a.poly.length],l=Ca(n,r,o);if(!l)return null;const h=0===i?r:n,c=0===i?n:r,d=Na(h,l),p=Na(c,l),u=Ha(h,l),_=function(t,e,s,i){const o=[];for(const a of t)for(let t=0;ti||Math.abs(Ha(r,e)-s)>i)continue;const l=Na(n,e),h=Na(r,e),c=Math.min(l,h),d=Math.max(l,h);d-c>i&&o.push({roomId:a.id,edge:t,lo:c,hi:d})}return o.sort((t,e)=>t.lo-e.lo||t.hi-e.hi||t.roomId.localeCompare(e.roomId)||t.edge-e.edge)}(t,l,u,o),m=function(t,e,s,i){const o=[e,s];for(const a of t){const t=Math.max(e,a.lo),n=Math.min(s,a.hi);n-t>i&&o.push(t,n)}const a=[...new Set(o)].sort((t,e)=>t-e),n=[];for(let e=0;e+1r>t.lo-i&&rt.roomId))].sort();n.push({lo:s,hi:o,owners:l})}return n}(_,Math.min(d,p),Math.max(d,p),o);return!m.length||m.some(t=>t.owners.length<1||t.owners.length>2)?null:{roomId:e,edge:s,movedEndpoint:i,axis:l,line:u,fixed:d,moving:p,intervals:_,baseline:m}}function Ea(t,e,s){const i=[];for(const o of t.intervals){const a=e.polys[o.roomId];if(!a){i.push(o);continue}if(o.edge<0||o.edge>=a.length)continue;const n=a[o.edge],r=a[(o.edge+1)%a.length];if(Ca(n,r,s)!==t.axis||Math.abs(Ha(n,t.axis)-t.line)>s||Math.abs(Ha(r,t.axis)-t.line)>s)continue;const l=Na(n,t.axis),h=Na(r,t.axis),c=Math.min(l,h),d=Math.max(l,h);d-c>s&&i.push({...o,lo:c,hi:d})}return i}function Oa(t,e,s){return new Set(t.filter(t=>e>t.lo-s&&et.roomId)).size}function Ba(t,e,s){for(const i of e.sideOwnership){const e=t.polys[i.roomId];if(!e)return!1;const o=e[i.edge],a=e[(i.edge+1)%e.length];if(Ca(o,a,s)!==i.axis)return!1;const n=Na(0===i.movedEndpoint?o:a,i.axis),r=Ea(i,t,s),l=(i.moving>=i.fixed?i.baseline[i.baseline.length-1]:i.baseline[0]).owners.length;if(l<1||l>2)return!1;const h=Math.min(i.fixed,i.moving,n),c=Math.max(i.fixed,i.moving,n),d=[h,c,i.fixed,i.moving,n];for(const t of i.intervals)d.push(t.lo,t.hi);for(const t of r)d.push(t.lo,t.hi);const p=[...new Set(d.filter(t=>t>=h&&t<=c))].sort((t,e)=>t-e),u=Math.min(i.fixed,i.moving),_=Math.max(i.fixed,i.moving),m=Math.min(i.fixed,n),g=Math.max(i.fixed,n);for(let t=0;t+1u-s&&a<_+s,h=a>m-s&&a2||d>2)return!1;if(n&&h){if(c!==d)return!1}else if(!n&&h){if(d!==l||c>0&&c!==d)return!1}else if(2===c?2!==d:d>1)return!1}}return!0}function Wa(t,e,s,i){const o=[s.n[0]*i,s.n[1]*i],a={polys:{},openings:{},movedSpans:{}};for(const e of s.roomIds){const i=t.find(t=>t.id===e),n=s.edgeByRoom[e];if(!i||null==n)continue;const r=(n+1)%i.poly.length;a.polys[e]=i.poly.map((t,e)=>e===n||e===r?ba(t,o):[...t]),a.movedSpans[e]=[[ba(i.poly[n],o),ba(i.poly[r],o)]]}for(const t of e)s.movingOpeningIds.includes(t.id)&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function ja(t,e,s,i,o,a,n,r){if(!function(t,e,s,i){if(wa([t.x,t.y],e,s)>2*i)return!1;const o=ya(fa(s,e));if(o<=i)return!1;const a=[(s[0]-e[0])/o,(s[1]-e[1])/o],n=(t.x-e[0])*a[0]+(t.y-e[1])*a[1];return n>=-i&&n<=o+i}(t,e,s,r))return!0;if(wa([t.x,t.y],i,o)>2*r)return!1;const l=0===a?i:o;return ya(fa([t.x,t.y],l))>=t.length/2+n-r}function Ga(t,e,s,i){const o=i.movingHalf||0;return"circle"===t.kind?wa(t.center,e,s)2)return!1;const r=Wa(t,e,s,i);if(Object.keys(r.polys).length!==s.roomIds.length)return!1;if(!Ba(r,s,a))return!1;const l=new Set(s.roomIds),h=t=>r.polys[t.id]||t.poly;for(const i of s.roomIds){const l=t.find(t=>t.id===i),h=r.polys[i],c=s.edgeByRoom[i];if(!l||!h||h.length!==s.topology[i]||h.length!==l.poly.length||!xa(h))return!1;const d=l.poly.length,p=(c-1+d)%d,u=[p,c,(c+1)%d];for(const t of u)if(H(h[t],h[(t+1)%d]))return!1;const _=$a(l.poly),m=$a(h);if(Math.abs(m)t.id===e),i=r.polys[e];for(const o of t){if(o.id===e||l.has(o.id))continue;const t=h(o);if(!Aa(s.poly,o.poly,a)||!Aa(i,t,a))if(L(s.poly,o.poly,a)){if(!L(i,t,a))return!1}else if(L(o.poly,s.poly,a)){if(!L(t,i,a))return!1}else if(L(i,t,a)||L(t,i,a)||Ma(i,t,a))return!1}}t.find(t=>t.id===s.roomId);const c=r.polys[s.roomId],d=s.edgeByRoom[s.roomId],p=c[d],u=c[(d+1)%c.length];if(Math.abs(i)>a)for(const t of o.obstacles||[])if(Ga(t,p,u,o))return!1;for(const t of e){if(!s.movingOpeningIds.includes(t.id))continue;const e=r.openings[t.id];if(!e||t.hosted)return!1;if(!Da({...t,x:e[0],y:e[1]},[c],2*a))return!1}return!0}const Ua=new WeakMap,Ka=t=>"number"==typeof t&&Number.isFinite(t),Va=(t,e)=>{const s=new Map;for(const i of t||[])!Ka(i?.cm)||!e&&i.cm<=0||s.set(i.cm,(s.get(i.cm)||0)+1);return s},Ja=t=>[String(t),"см"].join(" ");function Ya(t,e,s={}){if(s.allowClear)return[];const i=!0===s.exactMultiplicity,o=Va(t,i),a=Va(e,i),n=[];for(const[t,e]of o){const s=a.get(t)||0;0===s?n.push({invariant:"wall_records",kind:"lost",owner:Ja(t),reference:`было ${e}`,detail:"записи этой толщины исчезли целиком"}):i&&s!==e&&n.push({invariant:"wall_records",kind:"count",owner:Ja(t),reference:`было ${e}, стало ${s}`,detail:"изменилось число записей этой толщины"})}if(i)for(const[t,e]of a)o.has(t)||n.push({invariant:"wall_records",kind:"count",owner:Ja(t),reference:`было 0, стало ${e}`,detail:"появились записи новой толщины"});return n}class Xa{constructor(){this._selectedRoomId=null,this._session=null,this._eligibility=null}get dragging(){return null!==this._session}ownsPointer(t){return this._session?.pointerId===t}get selectedRoomId(){return this._selectedRoomId}get snapshotIdentity(){return this._session?.snapshotIdentity||null}get activePointerId(){return this._session?.pointerId??null}get moved(){return!0===this._session?.moved}get delta(){return this._session?.delta||0}get rooms(){return this._session?.rooms||null}get openings(){return this._session?.openings||null}get plan(){return this._session?.plan||null}get preview(){return this._session?.accepted?.preview||null}get liveLabels(){return this._session?.accepted?.labels||null}selectRoom(t){this._selectedRoomId=t}restoreSelection(t){this._selectedRoomId=t}escapeIdle(){return this._selectedRoomId?(this._selectedRoomId=null,"selection-cleared"):"exit-tool"}resolve(t,e,s){this._eligibility&&this._eligibility.context===t||(this._eligibility={context:t,values:new Map});const i=this._eligibility.values.get(e);if(i)return i;const o=s();return this._eligibility.values.set(e,o),o}begin(t){return!this._session&&(this._session={pointerId:t.pointerId,start:[...t.start],roomId:t.roomId,plan:t.plan,options:t.options,rooms:t.rooms,openings:t.openings,snapshotIdentity:t.snapshotIdentity,before:t.before,moved:!1,delta:0,changedRoomIds:[...t.plan.roomIds],rejectionNotified:!1,wallUnionBefore:t.wallUnionBefore,epochBefore:t.epochBefore,accepted:null},!0)}move(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=(i=e.start,o=t.point,a=e.plan.n,[i?.[0],i?.[1],o?.[0],o?.[1],a?.[0],a?.[1]].every(Number.isFinite)?(o[0]-i[0])*a[0]+(o[1]-i[1])*a[1]:0);var i,o,a;const n=t.snap([e.plan.a[0]+e.plan.n[0]*s,e.plan.a[1]+e.plan.n[1]*s]),r=(n[0]-e.plan.a[0])*e.plan.n[0]+(n[1]-e.plan.a[1])*e.plan.n[1],l=function(t,e,s,i,o,a){if(!Number.isFinite(i)||Math.abs(i)<1e-9)return 0;const n=Math.sign(i),r=Math.abs(i),l=Math.max(Math.abs(o),1e-6);let h=Ua.get(s);h&&h.opts===a||(h={opts:a,values:new Map},Ua.set(s,h));const c=h.values;let d=0;for(let i=Math.min(l,r),o=0;o<4096&&i<=r+1e-9;o++,i=Math.min(r,i+l)){const o=n*i,h=`${o.toFixed(9)}|${l.toFixed(9)}`;let p=c.get(h);if(void 0===p&&(p=qa(t,e,s,o,a),c.size<4096&&c.set(h,p)),!p)break;if(d=o,Math.abs(i-r)<=1e-9)break}return d}(e.rooms,e.openings,e.plan,r,t.step,e.options);if(e.moved&&l===e.delta)return{kind:"no-op"};const h=Wa(e.rooms,e.openings,e.plan,l);let c;try{if(c=t.project(e.snapshotIdentity,h.polys,h.openings,e.changedRoomIds,e.rooms),!c.ok)return this._projectionRejected(e);if(Ya(c.value.beforeWalls,c.value.afterWalls,{exactMultiplicity:!0}).length)return this._projectionRejected(e)}catch{return this._projectionRejected(e)}const d=e.accepted,p={preview:c.value.preview,labels:null,artifact:c.value.artifact,beforeWalls:c.value.beforeWalls,afterWalls:c.value.afterWalls};e.accepted=p;try{t.publish(p.preview,p.artifact),p.labels=t.measure(h,e.plan)}catch{e.accepted=d;try{t.publish(d?.preview||null,d?.artifact||null)}catch{}return this._projectionRejected(e)}return e.moved=!0,e.delta=l,{kind:"accepted",preview:c.value.preview,labels:p.labels,artifact:c.value.artifact}}finish(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=e.accepted;let i;if(e.moved&&Math.abs(e.delta)>1e-9&&s)if(t.currentSnapshotIdentity!==e.snapshotIdentity)i={kind:"rejected",reason:"stale-snapshot"};else if(qa(e.rooms,e.openings,e.plan,e.delta,e.options))if(Ya(s.beforeWalls,s.afterWalls,{exactMultiplicity:!0}).length)i={kind:"rejected",reason:"wall-records"};else{let o=!1;try{o=t.validatePreview(s.preview)}catch{o=!1}i=o?{kind:"commit",preview:s.preview,before:e.before}:{kind:"rejected",reason:"invalid-candidate"}}else i={kind:"rejected",reason:"invalid-topology"};else i={kind:"no-op"};return this._session=null,"commit"===i.kind&&(this._eligibility=null),i}cancel(t,e){const s=this._session;if(!s||void 0!==e&&s.pointerId!==e)return{kind:"no-op"};this._session=null,this._eligibility=null;const i=t===s.snapshotIdentity;return{kind:"cancelled",restoreWallUnion:i?s.wallUnionBefore:null,restoreEpoch:i?s.epochBefore:null}}reset(){this._selectedRoomId=null,this._session=null,this._eligibility=null}_projectionRejected(t){const e=!t.rejectionNotified;return t.rejectionNotified=!0,{kind:"rejected",notify:e}}}const Za=t=>"object"==typeof t&&null!==t?t:null,Qa=t=>Array.isArray(t)?t.flatMap(t=>{const e=Za(t);return e?[e]:[]}):[],tn=t=>{if(!Array.isArray(t))return null;const e=Number(t[0]),s=Number(t[1]);return Number.isFinite(e)&&Number.isFinite(s)?[e,s]:null},en=t=>Array.isArray(t)?t.flatMap(t=>{const e=tn(t);return e?[e]:[]}):[];function sn(t){const e=en(t.poly);if(e.length>=3)return e;const s=Number(t.x),i=Number(t.y),o=Number(t.w),a=Number(t.h);return[s,i,o,a].every(Number.isFinite)?[[s,i],[s+o,i],[s+o,i+a],[s,i+a]]:[]}function on(t){if(!t.length)return null;const e=t.map(t=>t[0]),s=t.map(t=>t[1]);return{minX:Math.min(...e),minY:Math.min(...s),maxX:Math.max(...e),maxY:Math.max(...s)}}function an(t,e){const s=Za(t);if(!s)return null;const i=Qa(s.rooms),o=new Set(function(t,e){const s=Qa(t),i=new Set(e);return s.flatMap(t=>{const e=String(t.id||"");return e&&i.has(e)?[e]:[]})}(i,e)),a=i.filter(t=>o.has(String(t.id||"")));if(!on(a.flatMap(sn))||!a.length)return null;const n=Number(s.cell_cm),r=Number.isFinite(n)&&n>0?n:5,l=a.flatMap(t=>{const e=sn(t);return e.map((t,s)=>[t,e[(s+1)%e.length]])}),h=.02*W,c=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(t[0]-e[0]-n*i,t[1]-e[1]-n*o)},d=(t,e)=>Math.min(...l.map(([s,i])=>((t,e,s,i)=>{const o=(t,e,s)=>(e[0]-t[0])*(s[1]-t[1])-(e[1]-t[1])*(s[0]-t[0]),a=o(t,e,s),n=o(t,e,i),r=o(s,i,t),l=o(s,i,e);return a*n<=h*h&&r*l<=h*h&&Math.min(t[0],e[0])<=Math.max(s[0],i[0])+h&&Math.min(s[0],i[0])<=Math.max(t[0],e[0])+h&&Math.min(t[1],e[1])<=Math.max(s[1],i[1])+h&&Math.min(s[1],i[1])<=Math.max(t[1],e[1])+h?0:Math.min(c(t,s,i),c(e,s,i),c(s,t,e),c(i,t,e))})(t,e,s,i))),p=t=>{const e=tn(t.a),s=tn(t.b);if(e&&s)return((t,e)=>l.some(([s,i])=>{const o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a);if(!(n>h))return!1;const r=t=>Math.abs((t[0]-s[0])*a-(t[1]-s[1])*o)/n;if(r(t)>h||r(e)>h)return!1;const l=t=>((t[0]-s[0])*o+(t[1]-s[1])*a)/(n*n),c=l(t),d=l(e);return Math.min(1,Math.max(c,d))-Math.max(0,Math.min(c,d))>h}))(e,s);const[i]=String(t.key||"").split("@"),o=i?.split(",").map(Number)||[];return 2!==o.length||!o.every(Number.isFinite)||l.some(([t,e])=>c(o,t,e)<=h)},u=Qa(s.walls).filter(p),_=Qa(s.wall_segments).filter(p),m=Math.max(0,...[...u,..._].map(t=>Number(t.cm)).filter(Number.isFinite))/r*W/2,g=Qa(s.partitions).filter(t=>{const e=tn(t.a),s=tn(t.b);if(!e||!s)return!0;const i=Math.max(0,Number(t.cm)||0)/r*W/2;return d(e,s)<=m+i+h}),f=Qa(s.room_drafts).filter(t=>{const e=en(t.points);if(e.length<2)return!0;const s=Qa(t.segments);return e.some((t,i)=>{if(i+1>=e.length)return!1;const o=Math.max(0,Number(s[i]?.cm)||0)/r*W/2;return d(t,e[i+1])<=m+o+h})}),b=Qa(s.wall_columns).filter(t=>{const e=tn(t.center);if(!e)return!0;const s=Math.max(0,Number(t.cm)||0)/r*W;return l.some(([t,i])=>c(e,t,i)<=m+s+h)}),v=new Set(g.map(t=>String(t.id||"")).filter(Boolean)),y=Qa(s.openings).filter(t=>{const e=Za(t.host);if("partition"===e?.kind)return v.has(String(e.id||""));const s=tn([t.x,t.y]);return!s||l.some(([t,e])=>c(s,t,e)<=h)});return{...s,rooms:a,walls:u,wall_segments:_,open_spans:Qa(s.open_spans).filter(p),partitions:g,room_drafts:f,wall_columns:b,openings:y}}const nn=(t,e)=>[(t[0]-e.x)/Math.max(1e-9,e.w)*e.stageWidth,(t[1]-e.y)/Math.max(1e-9,e.h)*e.stageHeight];function rn(t){const{poly:e,edge:s,text:i,view:o}=t,a=e[s],n=e[(s+1)%e.length],r=[(a[0]+n[0])/2,(a[1]+n[1])/2],l=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return(t=>{let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(e,s),h=Math.hypot(n[0]-a[0],n[1]-a[1])||1,c=[(n[0]-a[0])/h,(n[1]-a[1])/h],d=nn(r,o),p=nn(t.gearCenter,o),u=Math.max(34,7.2*i.length+12),_=Math.abs(l[0])>=Math.abs(l[1])?Math.max(28,u/2+4):Math.max(28,13),m=d[0]+l[0]*_,g=d[1]+l[1]*_,f=e=>{return s=m+c[0]*e,i=g+c[1]*e,o=u,a=18,n=p[0],r=p[1],l=t.gearWidthPx,h=t.gearHeightPx,2*Math.abs(s-n)=Math.abs(c[1])?c[0]:c[1])>0?-1:1,e=Math.max(o.stageWidth,o.stageHeight,64);for(let s=4;s<=e;s+=4){const e=s*t;if(!f(e)){b=e;break}const i=-e;if(!f(i)){b=i;break}}}const v=l[0]*_+c[0]*b,y=l[1]*_+c[1]*b,$=Math.hypot(v,y)||1,w=y/$*12,k=[r[0]+v/$*12*o.w/Math.max(1,o.stageWidth),r[1]+w*o.h/Math.max(1,o.stageHeight)];return{anchor:r,offsetXPx:v,offsetYPx:y,tangentOffsetPx:b,side:Math.abs(l[0])>=Math.abs(l[1])?l[0]<0?"left":"right":l[1]<0?"above":"below",leader:{a:r,b:k}}}const ln=t=>{let e=(t%360+360)%360;return e>180&&(e-=360),e};function hn(t,e,s,i,o,a=0,n=[t,e],r){const l=1e-7,h=(t,e,s,i,o)=>{const a=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*i)/o));return{t:a,x:e[0]+a*s,y:e[1]+a*i}},c=t=>{if(!t||!Number.isFinite(t[0])||!Number.isFinite(t[1]))return null;const e=Math.hypot(t[0],t[1]);return e>1e-9?[t[0]/e,t[1]/e]:null},d=c(r);let p=null;for(const r of i){const i=Number(r?.a?.[0]),u=Number(r?.a?.[1]),_=Number(r?.b?.[0]),m=Number(r?.b?.[1]);if(![i,u,_,m].every(Number.isFinite))continue;const g=_-i,f=m-u,b=g*g+f*f;if(!(b>1e-18))continue;const v=Math.sqrt(b),y=h(n,[i,u],g,f,b),$=Math.hypot(n[0]-y.x,n[1]-y.y);if(!($=2?r.axisA:r.a,e=r.axisB?.length>=2?r.axisB:r.b,s=Number(e[0])-Number(t[0]),i=Number(e[1])-Number(t[1]),o=s*s+i*i;if(o>1e-18&&[t[0],t[1],e[0],e[1]].every(Number.isFinite)){const e=h(n,t,s,i,o),a=(n[0]-e.x)*w[0]+(n[1]-e.y)*w[1];if(a>l)k=2;else if(a<-1e-7){if(r.roomSide)continue;k=-2}else d&&(k=w[0]*d[0]+w[1]*d[1])}}else{let t=n[0]-y.x,e=n[1]-y.y;const s=Math.hypot(t,e);if(s>l)t/=s,e/=s,k=2;else if(d)[t,e]=d,k=1;else{const s=i<_||i===_&&u<=m;t=-(s?f:-f)/v,e=(s?g:-g)/v}w=[t,e]}const x=h([t,e],[i,u],g,f,b);let S=x.t,M=x.x,D=x.y;if(Number.isFinite(a)&&a>0){const t=Math.round(S*v/a)*a;S=Math.max(0,Math.min(1,t/v)),M=i+S*g,D=u+S*f}const C=ln(180*Math.atan2(-w[0],w[1])/Math.PI),P={cx:M+w[0]*(s/2),cy:D+w[1]*(s/2),angle:C,dist:$,sideScore:k,stableId:"string"==typeof r.stableId?r.stableId:""};(!p||P.distp.sideScore+l||Math.abs(P.sideScore-p.sideScore)<=l&&P.stableId.localeCompare(p.stableId)<0))&&(p=P)}return p&&{cx:p.cx,cy:p.cy,angle:p.angle,dist:p.dist}}const cn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),dn=(t,e)=>{const s=t=>`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=s(t),o=s(e);return i<=o?`${i}|${o}`:`${o}|${i}`};function pn(t,e,s,i,o,a,n=1){const r=[],l=new Map;for(const h of t||[]){const c="string"==typeof h?.id?h.id:"";if(!c)continue;const d=K(t,c,e,s,i,o,a,n);if(d)for(let t=0;t1e-9))continue;const a=V(d.poly,t),n=Number(d.offsets[t]),h=Number.isFinite(n)&&n>0?n:0,p=[e[0],e[1]],u=[s[0],s[1]],_=dn(p,u),m=l.get(_)||new Set;m.add(c),l.set(_,m),r.push({a:[p[0]+a[0]*h,p[1]+a[1]*h],b:[u[0]+a[0]*h,u[1]+a[1]*h],axisA:p,axisB:u,normal:a,owner:"room",stableId:`room:${c}:${_}`,roomId:c,atomId:_,half:h})}}const h=[];for(const t of r){const{atomId:e,half:s,...i}=t;if(!(1===l.get(e)?.size&&s>1e-9)){h.push(i);continue}const o=i.normal;h.push({...i,roomSide:"inside",stableId:`${i.stableId}:inside`}),h.push({...i,a:[i.axisA[0]-o[0]*s,i.axisA[1]-o[1]*s],b:[i.axisB[0]-o[0]*s,i.axisB[1]-o[1]*s],normal:[-o[0],-o[1]],roomSide:"outside",stableId:`${i.stableId}:outside`})}return h}function un(t){const e=[],s=new Set;for(const i of t||[])if(Array.isArray(i)&&!(i.length<2))for(let t=0;t1e-9))continue;const n=[o[0],o[1]],r=[a[0],a[1]],l=`physical:${dn(n,r)}`;s.has(l)||(s.add(l),e.push({a:n,b:r,axisA:[...n],axisB:[...r],normal:null,owner:"physical",stableId:l}))}return e}const _n=new WeakMap;const mn=1e-9,gn=2e-7,fn=1e7,bn=t=>{const e=Math.sign(t)*Math.floor(Math.abs(t)*fn+.5)/fn;return Object.is(e,-0)?0:e},vn=t=>`${bn(t[0])},${bn(t[1])}`,yn=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),$n=(t,e,s)=>t/(e||1)*s,wn=(t,e)=>Math.atan2(e[1]-t[1],e[0]-t[0]),kn=t=>Array.isArray(t)&&t.length>=2&&t.every(t=>Number.isFinite(t)),xn=t=>(t||[]).filter(t=>kn(t?.a)&&kn(t?.b)&&yn(t.a,t.b)>mn);function Sn(t,{minAngleDeg:e=15,maxValence:s=6}={}){const i=new Map;for(const e of xn(t))for(const[t,s]of[[e.a,e.b],[e.b,e.a]]){const e=i.get(vn(t))||[];e.push(wn(t,s)),i.set(vn(t),e)}const o=[];for(const[t,a]of i){if(a.length>s&&o.push({rule:"valence",subject:t,actual:a.length,limit:s}),a.length<2)continue;const i=[...a].sort((t,e)=>t-e);let n=1/0;for(let t=0;tmn&&s(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI%180+180)%180,Dn=(t,e,s=1)=>{const i=Math.abs(Mn(t)-Mn(e));return Math.min(i,180-i)<=s},Cn=t=>{const e=new Map;for(const s of t)for(const t of[s.a,s.b]){const i=e.get(vn(t));i?i.push(s):e.set(vn(t),[s])}return e};function Pn(t,e,s){const i=xn(e),o=s??Cn(i),a=new Set([t]);let n=yn(t.a,t.b);const r=[t.a,t.b];for(;r.length;){const e=r.pop();for(const s of o.get(vn(e))||[])a.has(s)||Dn(s,t)&&Number(s.cm||0)===Number(t.cm||0)&&(a.add(s),n+=yn(s.a,s.b),r.push(s.a,s.b))}return n}function In(t,e,s,{minLengthCm:i=20}={}){const o=[],a=xn(t),n=Cn(a);for(const t of a){const r=Pn(t,a,n)/s*(e||1),l=Math.max(i,Number(t.cm)>0?Number(t.cm):0);r{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a<=mn?0:Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))};function zn(t,e,s,{minDistanceCm:i=5}={}){const o=xn(t),a=new Map;for(const t of o)a.set(vn(t.a),t.a),a.set(vn(t.b),t.b);const n=$n(i,e,s),r=n>mn?n:1,l=(t,e)=>`${Math.floor(t/r)},${Math.floor(e/r)}`,h=new Map;for(const[t,e]of a){const s=l(e[0],e[1]),i=h.get(s);i?i.push([t,e]):h.set(s,[[t,e]])}const c=new Map;for(const t of o){const e=Math.min(t.a[0],t.b[0])-n,s=Math.max(t.a[0],t.b[0])+n,i=Math.min(t.a[1],t.b[1])-n,o=Math.max(t.a[1],t.b[1])+n;for(let a=Math.floor(e/r);a<=Math.floor(s/r);a++)for(let e=Math.floor(i/r);e<=Math.floor(o/r);e++){const s=`${a},${e}`,i=c.get(s);i?i.push(t):c.set(s,[t])}}const d=[];for(const[t,o]of a){const a=Math.floor(o[0]/r),l=Math.floor(o[1]/r);for(let r=-1;r<=1;r++)for(let c=-1;c<=1;c++)for(const[p,u]of h.get(`${a+r},${l+c}`)||[]){if(t>=p)continue;const a=yn(o,u);a<=gn||a{const i=a[(s+1)%a.length];return t+(e[0]*i[1]-i[0]*e[1])},0))/2,r=(s||1)/i,l=n*r*r;return l0&&o.push(...e.slice(0,i))}return o}const Fn=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);function Nn(t){const{hass:e,devices:s,markers:i,showEntities:o,currentBinding:a,currentDeviceId:n,labels:r}=t,l=Z(i),h=new Set(i.filter(t=>t.removed).map(t=>t.binding)),c=new Set;for(const t of s)t.id!==n&&("device"===t.bindingKind&&t.bindingRef&&c.add(`device:${t.bindingRef}`),"entity"===t.bindingKind&&t.bindingRef&&c.add(`entity:${t.bindingRef}`));const d=new Set;for(const t of s)"device"===t.bindingKind&&t.name&&d.add(`${t.name.trim()}|${t.area||""}`);const p=[];for(const t of Object.values(e?.devices||{})){if(!t||"service"===t.entry_type)continue;const e=`device:${t.id}`;if(c.has(e))continue;const s=String(t.name_by_user||t.name||t.id).trim();e!==a&&!h.has(e)&&d.has(`${s}|${t.area_id||""}`)||p.push({value:e,label:s,sub:(t.model||r.device)+("Group"===t.model?r.z2mGroup:""),kind:"device",ref:t.id,areaId:t.area_id||"",model:t.model||""})}for(const[t,s]of Object.entries(e?.entities||{})){const i=`entity:${t}`;if(c.has(i))continue;if(Q(e,t,l)&&!h.has(i))continue;const o=Fn.has(s?.platform),a="group"===s?.platform;if(!o&&!a)continue;if(s?.hidden&&!h.has(i))continue;const n=e?.states?.[t];p.push({value:i,label:s?.name||n?.attributes?.friendly_name||t,sub:`${t.split(".")[0]} · ${a?r.group:r.helper}`,kind:"entity",ref:t,areaId:s?.area_id||s?.device_id&&e?.devices?.[s.device_id]?.area_id||"",model:"",parentDeviceId:s?.device_id||void 0})}if(o){const t=new Set(p.map(t=>t.value));for(const[s,i]of Object.entries(e?.entities||{})){const o=`entity:${s}`;if(c.has(o)||t.has(o)||i?.hidden&&!h.has(o))continue;const a=!!i?.device_id&&l.devices.has(i.device_id);if(Q(e,s,l)&&!h.has(o)&&!a)continue;const n=e?.states?.[s],d=i?.device_id?e?.devices?.[i.device_id]:null,u=d?String(d.name_by_user||d.name||""):"";p.push({value:o,label:i?.name||n?.attributes?.friendly_name||s,sub:`${s.split(".")[0]} · ${r.entity}${u?` · ${u}`:""}`,kind:"entity",ref:s,areaId:i?.area_id||d?.area_id||"",model:"",parentDeviceId:i?.device_id||void 0})}}return p.sort((t,e)=>t.label.localeCompare(e.label)||t.value.localeCompare(e.value))}const Hn={kind:"active",enabledEntityIds:[],allEntityIds:[]};function Ln(t){return t.startsWith("entity:")?"entity":"device"}function En(t){const e=t.binding.indexOf(":"),s=e>=0?t.binding.slice(e+1):"",i=Object.keys(t).filter(t=>!["id","binding","hidden"].includes(t));return t.id===`h${s}`&&0===i.length}const On=.001;function Bn(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function Wn(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function jn(t,e,s){return Math.abs(t[0]-e[0])"room"!==t.kind&&s.some(s=>s!==t&&function(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>s))return!1;const n=i/a,r=o/a,l=e=>Math.abs((e[0]-t.a[0])*r-(e[1]-t.a[1])*n);if(l(e.a)>s||l(e.b)>s)return!1;const h=(e.a[0]-t.a[0])*n+(e.a[1]-t.a[1])*r,c=(e.b[0]-t.a[0])*n+(e.b[1]-t.a[1])*r;return Math.min(a,Math.max(h,c))-Math.max(0,Math.min(h,c))>s}(t,s,e))),o=i.map(t=>{const[e,s]=Gn(t.a,t.b);return{a:e,b:s,key:`hidden|${Un(t,e,s)}`,sourceKind:t.kind,sourceId:t.id}}).sort((t,e)=>t.key.localeCompare(e.key)),a=i.flatMap(t=>[t.a,t.b].map((e,s)=>({point:[e[0],e[1]],key:`hidden|${qn(t)}|endpoint-${s}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:o,endpoints:a}}function Yn(t){const e=t.epsilon??On,s=t.roomCuts||[],i=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!Bn(e.a)||!Bn(e.b))continue;const t=i.get(e.hostId)||[];t.push([e.a[0],e.a[1],e.b[0],e.b[1]]),i.set(e.hostId,t)}const o=[];for(const[e,i]of tt(t.space.rooms).entries())i.length<4||o.push({a:[i[0],i[1]],b:[i[2],i[3]],kind:"room",id:`room-edge-${e}`,cuts:s});for(const e of t.space.room_drafts||[])if(e.id!==t.activeDraftId)for(let t=0;t+1Vn(s,t,e)))continue;const t=Wn(s);n.has(t)||n.set(t,{point:[s[0],s[1]],key:t})}}}return{segments:[...a.values()].sort((t,e)=>t.key.localeCompare(e.key)),endpoints:[...n.values()].sort((t,e)=>t.key.localeCompare(e.key))}}function Xn(t,e,s){return e.some(e=>Bn(e)&&jn(t,e,s))}function Zn(t,e,s){if(!s)return!0;const i=t-s.distance;return i<-1e-9||Math.abs(i)<=1e-9&&e.localeCompare(s.key)<0}function Qn(t,e,s){const i=s.epsilon??On,o=s.excludePoints||[],a=[...t.endpoints,...(s.extraEndpoints||[]).filter(t=>Bn(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],n=new Map;for(const t of a){if(Xn(t.point,o,i))continue;const e=Wn(t.point),s=n.get(e);(!s||t.key.localeCompare(s.key)<0)&&n.set(e,t)}return[...n.values()].map(t=>({kind:"endpoint",point:[...t.point],key:t.key,distance:Math.hypot(e[0]-t.point[0],e[1]-t.point[1])})).filter(t=>t.distance<=s.tolerance).sort((t,e)=>t.distance-e.distance||t.key.localeCompare(e.key))}function tr(t,e){if(!t.length)return null;if(e>0){const s=new Set;for(let i=0;i1)return{kind:"ambiguous",candidate:null,conflicts:t.filter(t=>s.has(t.key)).map(t=>({point:[...t.point],key:t.key}))}}return{kind:"resolved",candidate:t[0],conflicts:[]}}function er(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>0))return[...t.a];const n=i/a,r=o/a,l=Math.max(0,Math.min(a,(e[0]-t.a[0])*n+(e[1]-t.a[1])*r)),h=s>0?Math.max(0,Math.min(a,Math.round(l/s)*s)):l;return[t.a[0]+n*h,t.a[1]+r*h]}function sr(t,e,s){if(!(Bn(e)&&Bn(s.anchor)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??On,o=function(t,e){const s=e[0]-t[0],i=e[1]-t[1];if(!(Math.hypot(s,i)>Number.EPSILON))return null;const o=Math.round(Math.atan2(i,s)/(Math.PI/4))*(Math.PI/4);return[Math.cos(o),Math.sin(o)]}(s.anchor,e);if(!o)return{kind:"none",candidate:null,conflicts:[]};const a=Qn(t,e,s).filter(t=>function(t,e,s,i){const o=t[0]-e[0],a=t[1]-e[1];return o*s[0]+a*s[1]>i&&Math.abs(o*s[1]-a*s[0])<=i*Math.max(Math.hypot(o,a),1)}(t.point,s.anchor,o,i)),n=tr(a,s.distinguishTolerance||0);if(n)return n;let r=null;for(const a of t.segments){const t=a.b[0]-a.a[0],n=a.b[1]-a.a[1],l=a.a[0]-s.anchor[0],h=a.a[1]-s.anchor[1],c=o[0]*n-o[1]*t;let d=null;if(Math.abs(c)<=i*Math.max(Math.hypot(t,n),1)){if(Math.abs(l*o[1]-h*o[0])>i*Math.max(Math.hypot(l,h),1))continue;const t=[a.a,a.b].map(t=>({point:t,along:(t[0]-s.anchor[0])*o[0]+(t[1]-s.anchor[1])*o[1]})).filter(t=>t.along>i).sort((t,e)=>t.along-e.along);t.length&&(d=[...t[0].point])}else{const e=(l*n-h*t)/c,a=(l*o[1]-h*o[0])/c;e>i&&a>=-i&&a<=1+i&&(d=[s.anchor[0]+o[0]*e,s.anchor[1]+o[1]*e])}if(!d)continue;const p=Math.hypot(e[0]-d[0],e[1]-d[1]);p>s.tolerance||Xn(d,s.excludePoints||[],i)||!Zn(p,a.key,r)||(r={kind:"line",point:d,key:a.key,distance:p,segment:a})}return r?{kind:"resolved",candidate:r,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}const ir=.001;function or(t,e,s,i){const o=Number.isFinite(t)&&t>0?Math.floor(t):0,a=t=>"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,n=a(s),r=a(i)??0,l=[];let h=null;for(let t=0;t=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function rr(t,e,s,i){return t*i-e*s}function lr(t){let e=0;for(let s=0;st.localeCompare(e)),e[0]}function ur(t,e,s){const i=[...t];for(let t=!0;t&&i.length>=3;){t=!1;for(let o=0;o=0){i.splice(o,1),t=!0;break}}}return i}function _r(t,e){const s=t.map(t=>Math.max(0,Math.min(1,t))).sort((t,e)=>t-e),i=[];for(const t of s)(!i.length||Math.abs(t-i[i.length-1])>e)&&i.push(t);return i}function mr(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function gr(t,e,s){const i=function(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o:0}(t,e);if(i<-s||i>1+s)return null;const o=mr(e,i);return Math.hypot(o[0]-t[0],o[1]-t[1])<=s?Math.max(0,Math.min(1,i)):null}function fr(t,e,s,i,o){const a=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=e.b[0]-e.a[0],l=e.b[1]-e.a[1],h=e.a[0]-t.a[0],c=e.a[1]-t.a[1],d=rr(a,n,r,l),p=Math.max(Math.hypot(a,n),Math.hypot(r,l),1);if(Math.abs(d)>o*p){const t=rr(h,c,r,l)/d,e=rr(h,c,a,n)/d;return void(t>=-o&&t<=1+o&&e>=-o&&e<=1+o&&(s.push(Math.max(0,Math.min(1,t))),i.push(Math.max(0,Math.min(1,e)))))}if(!(Math.abs(rr(h,c,a,n))>o*p)){for(const i of[e.a,e.b]){const e=gr(i,t,o);null!=e&&s.push(e)}for(const s of[t.a,t.b]){const t=gr(s,e,o);null!=t&&i.push(t)}}}function br(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function vr(t,e){return t.minY-e.minY||t.index-e.index}function yr(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function $r(t){const e=t.right;return t.right=e.left,e.left=yr(t),yr(e)}function wr(t){const e=t.left;return t.left=e.right,e.right=yr(t),yr(e)}function kr(t,e){return t?(vr(e,t.item)<0?(t.left=kr(t.left,e),t.left.priority0)t.right=xr(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority=e&&Sr(t.left,e,s,i),t.item.minY<=s&&t.item.maxY>=e&&i.push(t.item),t.item.minY<=s&&Sr(t.right,e,s,i))}function Mr(t,e=.001){const s=Number.isFinite(e)&&e>0?e:ir,i=t.filter(t=>nr(t.a)&&nr(t.b)&&"string"==typeof t.key&&t.key.length>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>s),o=i.map(()=>[0,1]);!function(t,e,s){const i=t.map((t,e)=>({index:e,minX:Math.min(t.a[0],t.b[0])-s,maxX:Math.max(t.a[0],t.b[0])+s,minY:Math.min(t.a[1],t.b[1])-s,maxY:Math.max(t.a[1],t.b[1])+s})),o=[...i].sort((t,e)=>t.minX-e.minX||t.minY-e.minY||t.index-e.index),a=[...i].sort((t,e)=>t.maxX-e.maxX||t.index-e.index),n=new Set;let r=null,l=0;for(const i of o){for(;lt.index-e.index);for(const a of o)fr(t[a.index],t[i.index],e[a.index],e[i.index],s);n.add(i.index),r=kr(r,i)}}(i,o,s);const a=new Map;for(let t=0;t({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function Dr(t,e=.001){const s=Number.isFinite(e)&&e>0?e:ir,i=Mr(t,s),o=new Map,a=new Map,n=new Map(i.map(t=>[t.key,t]));for(const t of i){const e=cr(t.a,s),i=cr(t.b,s);o.set(e,t.a),o.set(i,t.b),a.has(e)||a.set(e,new Set),a.has(i)||a.set(i,new Set),a.get(e).add(i),a.get(i).add(e)}const r=new Map;for(const[t,e]of a){const s=o.get(t);r.set(t,[...e].sort((t,e)=>{const i=o.get(t),a=o.get(e);return Math.atan2(i[1]-s[1],i[0]-s[0])-Math.atan2(a[1]-s[1],a[0]-s[0])||t.localeCompare(e)}))}const l=new Set,h=new Map,c=(t,e)=>`${t}>${e}`;for(const t of i){const e=[cr(t.a,s),cr(t.b,s)];for(const[t,a]of[e,[e[1],e[0]]]){if(l.has(c(t,a)))continue;const e=[],d=[];let p=t,u=a,_=!1;for(let s=0;s<=2*i.length+2;s++){const s=c(p,u);if(l.has(s)){_=p===t&&u===a;break}l.add(s),e.push(p),d.push(dr(p,u));const i=r.get(u)||[],o=i.indexOf(p);if(o<0||!i.length)break;if(p=u,u=i[(o-1+i.length)%i.length],p===t&&u===a){_=!0;break}}if(!_||new Set(e).size<3||new Set(e).size!==e.length)continue;const m=e.map(t=>o.get(t)),g=lr(m);if(!(g>s*s))continue;const f=pr(ur(e,o,s)),b=new Set;for(const t of d)for(const e of n.get(t)?.sourceKeys||[])b.add(e);const v={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...b].sort((t,e)=>t.localeCompare(e))};h.has(f)||h.set(f,v)}}return{atoms:i,faces:[...h.values()].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))}}function Cr(t,e,s=.001){if(!nr(e))return null;const i=t.faces.filter(t=>{if(t.ring.some((i,o)=>function(t,e,s,i){const o=s[0]-e[0],a=s[1]-e[1],n=o*o+a*a;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1])<=i;const r=((t[0]-e[0])*o+(t[1]-e[1])*a)/n;return!(r<0||r>1)&&Math.hypot(e[0]+o*r-t[0],e[1]+a*r-t[1])<=i}(e,i,t.ring[(o+1)%t.ring.length],s)))return!1;let i=!1;for(let s=0,o=t.ring.length-1;se[1]!=n[1]>e[1]&&e[0]<(n[0]-a[0])*(e[1]-a[1])/(n[1]-a[1])+a[0]&&(i=!i)}return i});return[...i].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))[0]||null}function Pr(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(e[0]+i*n-t[0],e[1]+o*n-t[1])}function Ir(t,e,s=.5){let i=Math.abs(t-e)%180;return i>90&&(i=180-i),i<=s}function Tr(t,e,s,i){const o=t.filter(t=>"outer"===t.kind&&!t.open&&t.cm>0).map(t=>({interval:t,reusePartitionId:e.find(e=>function(t,e,s){return!(Math.abs(t.cm-e.cm)>1e-6)&&(Ir(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,.001)&&Pr(e.a,t.a,t.b)<=s&&Pr(e.b,t.a,t.b)<=s)}(e,t,i))?.id||null})),a=new Map;for(const e of s){if("partition"===e.host?.kind)continue;const s=t.map(t=>{const s=180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI;return{interval:t,distance:Ir(e.angle,s)?Pr([e.x,e.y],t.a,t.b):1/0}}).filter(t=>t.distance<=i).sort((t,e)=>t.distance-e.distance||t.interval.key.localeCompare(e.interval.key)),o=s[0]?.interval;"outer"===o?.kind&&!o.open&&o.cm>0&&a.set(e.id,o.key)}return{materialize:o,openingIntervals:a,removeOpeningIds:[...a.keys()].sort((t,e)=>t.localeCompare(e))}}function zr(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?Math.max(0,Math.min(1,((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o)):0}const Rr=t=>!t.key.startsWith("static:room|");function Ar(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;if(!(o>0))return null;const a=((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o,n=[e.a[0]+s*a,e.a[1]+i*a];return{point:n,t:a,distance:Math.hypot(n[0]-t[0],n[1]-t[1])}}function Fr(t,e){return e>0?Math.hypot(t[0]-Math.round(t[0]/e)*e,t[1]-Math.round(t[1]/e)*e):0}function Nr(t,e,s){if(Rr(t.source)!==Rr(e.source))return Rr(t.source)?t:e;const i=Fr(t.point,s),o=Fr(e.point,s);return Math.abs(i-o)>1e-9?i>o?t:e:function(t,e){return t[0]-e[0]||t[1]-e[1]}(t.point,e.point)>0?t:e}function Hr(t){return[t.from[0],t.from[1],t.to[0],t.to[1],t.targetKind].map(t=>"number"==typeof t?t.toFixed(6):t).join("|")}function Lr(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function Er(t,e){const s=e.epsilon??.001;if(!(e.maxDistance>=0)||!e.point&&!e.requiredSourceKey)return{kind:"none"};const i=new Map,o=t.flatMap(t=>[{source:t,endpoint:"a",point:t.a},{source:t,endpoint:"b",point:t.b}]);for(let t=0;ts)||l>e.maxDistance)continue;const h=Nr(n,r,e.gridStep||0),c=h===n?r:n;if(!Rr(h.source))continue;const d={sourceKey:h.source.key,endpoint:h.endpoint,from:[h.point[0],h.point[1]],to:[c.point[0],c.point[1]],targetSourceKey:c.source.key,targetKind:"endpoint",distance:l};i.set(Hr(d),d)}for(const a of o)if(Rr(a.source))for(const o of t){if(o===a.source)continue;const t=Ar(a.point,o);if(!t||t.t<=s||t.t>=1-s||!(t.distance>s)||t.distance>e.maxDistance)continue;const n={sourceKey:a.source.key,endpoint:a.endpoint,from:[a.point[0],a.point[1]],to:t.point,targetSourceKey:o.key,targetKind:"line",distance:t.distance};i.set(Hr(n),n)}const a=[];for(const o of i.values()){const i=Dr(Lr(t,o),s),n=e.point?Cr(i,e.point,s):i.faces.find(t=>!e.requiredSourceKey||t.sourceKeys.includes(e.requiredSourceKey))||null;n&&a.push({proposal:o,face:n})}if(a.sort((t,e)=>t.proposal.distance-e.proposal.distance||Hr(t.proposal).localeCompare(Hr(e.proposal))),!a.length)return{kind:"none"};return new Set(a.map(t=>`${Hr(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:a.map(t=>t.proposal)}:{kind:"repair",...a[0]}}const Or=(t,e)=>(t.composedPath?.()||[t.target]).some(t=>(t=>!!t&&"function"==typeof t.matches)(t)&&e(t));class Br{constructor(t){this.host=t,this._openGroupId=null,this._groupGeneration=0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,this._globalDismissListening=!1,this._globalDismissGuard=t=>{this.handleOutsideDismiss(t)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(t){return this._openGroupId&&t.find(t=>t.id===this._openGroupId)||null}reset(){this._openGroupId=null,this._contentAnimation?.cancel(),this._contentAnimation=void 0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}closeForNavigation(){const t="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,e=!!this._openGroupId||!!t;this._openGroupId=null,t?.(),t&&(this._currentModel=null),this._clearDismissClick(),e&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(t,e){this._openGroupId!==e?t.some(t=>t.id===e)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=e,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(t){const e=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),t&&e&&this.host.updateComplete().then(()=>{const t=this.host.root(),s=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e&&null!==t.offsetParent);(s||t.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(t,e,s){const i=this._openGroupId===t.id,o=t.items.find(e=>e.id===t.activeItemId&&!e.disabled);return p``}renderGroupModel(t,e,s){const i=t.items.find(t=>!t.disabled)?.id,o=t.items.some(e=>e.id===t.activeItemId&&!e.disabled)?t.activeItemId:void 0;return{contextId:e,kind:"group",ariaLabel:s.openGroup(t.label),visibleLabel:t.label,content:p`
    this._groupKeydown(t)}> + ${t.items.map(e=>p``)} +
    `}}runContext(t,e,s){if(t===e)return s()}render(t,e){this._currentModel=t,this._blocked=e;const s=t?.kind||"hidden";return p`
    + +
    `}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(Or(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=Or(t,t=>t.classList.contains("editor-secondary")),i=Or(t,t=>!!t.dataset.editorGroup),o=Or(t,t=>!!t.dataset.editorPalette),a=Or(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const Wr=t=>t.stopPropagation(),jr=1e-6*W,Gr=6*W;function qr(t){return Number.isFinite(t)?Math.round(t/W)*W:t}const Ur=(t,e,s,i)=>Math.hypot(s-t,i-e),Kr=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),Vr=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,l=Math.PI/180,h=Math.cos(s*l)*r,c=Math.sin(s*l)*r,d=Math.cos(n*l)*r,p=Math.sin(n*l)*r,u=Math.max(Ur(t+h,e+c,o+d,a+p),Ur(t-h,e-c,o-d,a-p)),_=Math.max(Ur(t+h,e+c,o-d,a-p),Ur(t-h,e-c,o+d,a+p));return Math.min(u,_)},Jr=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Yr(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,l="",h=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=jr&&c++},u=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},_={};let m=5;for(const t of s){const e=Jr(t);null!=t?.id&&(_[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>jr||i))return;o++,t>n&&(n=t);const a=t*ot*e;a>r&&(r=a,l=s)};for(const t of s){const e=Jr(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[qr(e[0]),qr(e[1])];return t=Math.max(t,Ur(e[0],e[1],s[0],s[1])),u(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=qr(e+o),r=qr(s+a),l=qr(e),h=qr(s),c=Math.max(W,n-l),d=Math.max(W,r-h);t=Kr(e,s,o,a,l,h,c,d),i.x=l,i.y=h,i.w=c,i.h=d,p(e,l),p(s,h),p(e+o,l+c),p(s+a,h+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[qr(e[0]),qr(e[1])];return t=Math.max(t,Ur(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],l=n.length?[[o[0],n[0]]]:[],h=[];for(let t=0;t+1=2)for(const[t,e]of l)u(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[qr(o[0]),qr(o[1])],l=[qr(n[0]),qr(n[1])];let h=Math.max(Ur(o[0],o[1],r[0],r[1]),Ur(n[0],n[1],l[0],l[1]));const c=Ur(r[0],r[1],l[0],l[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-jr&&i+e/2<=c+jr});c>jr&&d?(i.a=r,i.b=l,u(o,r),u(n,l)):h=0,g(h,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[qr(t[0]),qr(t[1])],n=Ur(t[0],t[1],o[0],o[1]);i.center=o,u(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[qr(e[0]),qr(e[1])],a=[qr(s[0]),qr(s[1])];t=Math.max(Ur(e[0],e[1],o[0],o[1]),Ur(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],u(e,o),u(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=qr(e),r=qr(s);if(null!=i.w&&null!=i.h){const l=qr(e+o),h=qr(s+a),c=Math.max(W,l-n),d=Math.max(W,h-r);t=Kr(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=Ur(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=jr)continue;const l=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),u=!(Number.isFinite(p)&&p===d),_=Vr(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,l,c,d);i.x=l,i.y=c,i.angle=d,u&&h++,g(_,e,s,u);continue}const o=it([i.x,i.y],t.rooms||[],Gr,{step:W,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),l=Vr(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&h++,g(l,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=qr(s.x),n=qr(s.y),r=Ur(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const l="string"==typeof s.s?s.s:"";g(r,_[l]??m,l)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:l,rotated:h,removedDrafts:d},changed:o>0||c>0}}function Xr(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Zr=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Qr=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function tl(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Qr(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function el(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Zr(i.a)&&Zr(i.b)&&(Qr(t,i.a)<=o||Qr(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Qr(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function il(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Zr(t.a)&&Zr(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tQr(e,t.at)>s);let c=h[0]??r.a,d=h[h.length-1]??l.b;if(h.length<2&&(c=r.a,d=l.b),d[0]0?(d[0]-c[0])/u:0,m=u>0?(d[1]-c[1])/u:0,g=t=>u>0?((t[0]-c[0])*_+(t[1]-c[1])*m)/u:0;for(const t of[r,l]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function ol(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Xr(t.t,n)},a++;const r=nt(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,rt(s,r,i.coordScale))}return a}function al(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>at(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const nl=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),rl=(t,e)=>t[0]-e[0]||t[1]-e[1],ll=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],hl=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,cl=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const l=a/r,h=n/r,c=e=>Math.abs((e[0]-t[0])*h-(e[1]-t[1])*l);if(c(s)>o||c(i)>o)return null;const d=hl(s,t,l,h),p=hl(i,t,l,h),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>o?{lo:u,hi:_}:null},dl=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,pl=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},ul=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,l=a/n,h=x(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!nl(o))return!0;const a=hl(o,t.a,r,l),c=Math.abs((o[0]-t.a[0])*l-(o[1]-t.a[1])*r),d=x(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=h+d+i})},_l=(t,e,s)=>{if(t.host){const i=nt(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},ml=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),l=Math.sin(n);if(Math.abs(o*l-a*r)>1e-6)return!1;const h=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(h*a-c*o)>s)return!1;const d=h*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),fl=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},bl=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function vl(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],l=Array.isArray(t?.room_drafts)?t.room_drafts:[],h={walls:a,partitions:n,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!l.length)return h;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,u=r;const _=lt(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const l=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(l>i)){e=!1;break}const h=s.flatMap(t=>{const e=t.cm>0?t.cm:ht;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of h){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=l-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!pl(t))continue;const s=v.get(t.id);if(!(s&&nl(s.a)&&nl(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=rl(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],l=n[1]-a[1],h=Math.hypot(r,l);if(!(h>c))continue;const _=r/h,g=l/h,y=lt(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=cl(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=u.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:nt(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,h];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=hl(e.center,a,_,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(h,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=h+c).map(t=>Math.max(0,Math.min(h,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>gl(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=ll(a,_,g,i),l=ll(a,_,g,n),h=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of h)d.set(t.roomId,t);const p=[...d.values()],u=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:ht)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===u.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:l,cm:s.cm},w=D.some(t=>!!cl(r,l,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:ht:0,I=v?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:l,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tdl(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&ct(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const l=dt(s,o.cellCm,o.gridPitch),h={lo:i.lo-l,hi:i.hi+l};for(const t of P)dl(t,h,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>dl(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=dt(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)dl(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const z=bl(P,c),R=z.filter(t=>t.safe),A=z.filter(t=>!t.safe);if(!R.length)continue;if(p.length-1+A.length>2e3)continue;let F=d;for(const t of R){const[e,i]=rl(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];F=pt(F,e,i,t.finalCm,o.pitch,o.coordScale)}if(F=ut(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),F.length>500)continue;const N=new Set(p.map(t=>t.id));N.delete(s.id);const H=A.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:fl(s.id,i,a,N);return N.add(n),{id:n,a:i,b:a,cm:s.cm}}),L=p.findIndex(t=>t.id===s.id),E=p.slice();E.splice(L,1,...H);const O=E.map(t=>gl(t,o.coordScale)),B=new Map;let W=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=rt(t,s.resolved,o.coordScale),{host:i,...a}=e;B.set(t.id,a);continue}const i=S.get(t.id),a=H[A.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&O.find(t=>t.id===a.id);if(!a||!n){W=!1;break}const r=n.b[0]-n.a[0],l=n.b[1]-n.a[1],h=Math.hypot(r,l),d=hl(e.center,n.a,r/h,l/h),p={...rt(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/h}};if(!_t(p,O,o.coordScale,o.cellCm,o.gridPitch).resolved){W=!1;break}B.set(t.id,p)}if(!W)continue;const j=u.map(t=>B.get(t.id)||t),G=[...I.keys()].map(t=>B.get(t)),q=G.map(t=>_l(t,O,o));if(q.some(t=>!t))continue;let U=!1;for(let t=0;tt.id));for(const t of j){if(K.has(t.id))continue;const e=_l(t,O,o);e&&q.some(t=>ml(t,e,c))&&(U=!0)}if(U)continue;const V=mt(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=B.get(t),i=gt(V,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=F,p=E,u=j,f+=R.length,b+=I.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const yl=t=>JSON.parse(JSON.stringify(t)),$l=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),wl=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function kl(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const xl=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const Sl=1e-9;class Ml extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const Dl=t=>JSON.parse(JSON.stringify(t)),Cl=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),Pl=t=>[Number(t[0]),Number(t[1])],Il=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,Tl=(t,e)=>{const s=Il(t),i=Il(e);return sIl(t)<=Il(e)?[Pl(t),Pl(e)]:[Pl(e),Pl(t)],Rl=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),Al=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/W;Math.abs(i-Math.round(i))>=kt&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of at(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},Nl=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},Hl=(t,e,s)=>{const i=Math.max(0,Math.min(1,Nl(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},Ll=(t,e,s,i,o=Sl)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const l=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(l(s)>o||l(i)>o)return 0;const h=Nl(s,t,e),c=Nl(i,t,e);return Math.max(0,Math.min(1,Math.max(h,c))-Math.max(0,Math.min(h,c)))*r},El=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),l=new Uint32Array(64),h=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=h(s,17)^h(s,19)^s>>>10;l[t]=l[t-16]+i+l[t-7]+o>>>0}let[e,s,i,o,c,d,p,u]=r;for(let t=0;t<64;t++){const a=u+(h(c,6)^h(c,11)^h(c,25))+(c&d^~c&p)+n[t]+l[t]>>>0,r=e&s^e&i^s&i;u=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((h(e,2)^h(e,13)^h(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+u>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},Bl=(t,e,s,i,o="")=>{const[a,n]=zl(e,s),r=`${t}|${Il(a)}|${Il(n)}|${[...i].sort().join(",")}${o}`;return`wall-${Ol(El(r)).slice(0,20)}`};let Wl=0;const jl=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||Ol(El(`${Date.now()}|${Wl++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new Ml("duplicate-id","id factory exhausted")},Gl=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&Cl(s.a)&&Cl(s.b)){if(e.has(s.id))throw new Ml("duplicate-id",s.id);e.set(s.id,s)}return e},ql=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=Sl&&Math.abs(e[1]-i[1]-a)<=Sl?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},Ul=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=yt(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:$t(s,null,1,.04*W,!0),a=[];for(const t of s){const s=at(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>Hl([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*W)):[],l=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),h=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!xt(t,e,Number(s.angle)))return!1;if(Hl(i,t,e)>.02*W)return!1;const o=Rl(t,e),a=Nl(i,t,e)*o,n=Number(s.length)/2;return a+n>=-Sl&&a-n<=o+Sl}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=at(i);if(!r||!p||p.length<3)throw new Ml("invalid-room",r);const u=wt(s,r,n,W,1,[...t.walls||[],...l]);if(!u||u.poly.length<3)throw new Ml("invalid-room",r);const _=Array.isArray(i.wall_ids)?i.wall_ids:[],m=_.length===p.length;let g=null,f=m;if(f)for(let t=0;tSl||Math.abs(i[1]-g[1])>Sl)){f=!1;break}g=i}const b=[];for(let t=0;t2)throw new Ml("third-owner",n);const d=m?_[u.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||Ll(s,i,o.a,o.b)>Sl?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:Pl(p[u.parent[t]]),b:Pl(p[(u.parent[t]+1)%p.length])})}const g=u.parent[t];l.parentKeys.add(vt(p[g],p[(g+1)%p.length],W));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>Hl(v,[t[0],t[1]],[t[2],t[3]])<=.04*W);l.zeroWall||=y(a)||y(o)&&!h(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:u.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},Kl=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[Tl(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new Ml("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new Ml("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],l=i?s.get(i):void 0;if(l){r.set(t,l);continue}const h=i?t.preferredCarriers.get(i):void 0;if(i&&h){r.set(t,{id:i,a:h.a,b:h.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>Ll(t.a,t.b,e.a,e.b)>Sl),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||Rl(e.a,e.b)-Rl(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const l=new Map;for(const[t,e]of r){const s=l.get(e.id)||[];s.push(t),l.set(e.id,s)}for(const[t,e]of l){const i=s.get(t)||r.get(e[0]);if(!i)throw new Ml("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=Hl(o,t.a,t.b)<=Sl?0:1,a=Hl(o,e.a,e.b)<=Sl?0:1;if(s!==a)return s-a;return(Hl(i.a,t.a,t.b)<=Sl?0:1)-(Hl(i.a,e.a,e.b)<=Sl?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const h=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(h.has(t.id))throw new Ml("duplicate-id",t.id);h.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=zl(e.a,e.b),o=`${String(t.id||"")}|${Il(s)}|${Il(i)}|${[...e.owners].sort().join(",")}`,a=Ol(El(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new Ml("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;h.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,h.add(o)}}else for(const t of c)t.id=jl(h),h.add(t.id)},Vl=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=Nl(s,e.a,e.b),o=Rl(e.a,e.b),a=Number(t.length)/2;return i>=-Sl&&i<=1+Sl&&Hl(s,e.a,e.b)<=.02*W&&xt(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-Sl&&i*o+a<=o+Sl},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,Nl(s,n.a,n.b)))}},Jl=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=Nl(s,e.a,e.b),a=Rl(e.a,e.b);return o>=-Sl&&o<=1+Sl&&Hl(s,e.a,e.b)<=.02*W&&xt(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-Sl&&o*a+i<=a+Sl}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>Hl(s,t.a,t.b)-Hl(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,Nl(s,a.a,a.b)))}:null},Yl=(t,e,s)=>{const i=Gl(t),{atoms:o,rooms:a}=Ul(t,i);Kl(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=vt(e.a,e.b,W),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!Cl(t.a)||!Cl(t.b))&&Ll(e.a,e.b,t.a,t.b)>=Rl(e.a,e.b)-Sl)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new Ml("thickness-conflict",e.key);if(a.length)return a[0];const n=St(i,e.a,e.b,W,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:vt(t.a,t.b,W),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=Vl(t,e);s&&(t.host=s);continue}const i=Vl(t,e)??(s?Jl(t,e):null);if(i)t.host=i;else{if(!s)throw new Ml("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Xl(t,e={}){const s=JSON.stringify(t),i=Dl(t);bt(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Yl(t,a,s)}return i.model_version=9,bt(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Zl=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Ql(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Zl(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Zl(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):Dl(e)}return o[e,e in t?s(t[e],i):Dl(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return Dl(e)};return s(t,e)}const th=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),eh=(t,e,s)=>Math.min(s,Math.max(e,t)),sh=t=>Et({spaces:[t]})[0],ih=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,l=Math.max(i*r*.02,1e-9),h=[];for(const e of t||[])for(const t of at(e)||[])h.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l),p=new Map;for(const n of t||[]){if(!n?.id)continue;const u=K(t,n.id,e,s,i,o,a,r);if(u)for(let t=0;t0&&_>0&&n===m&&_!==n))continue;const g=u.poly[i],f=u.poly[(i+1)%u.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>l&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,h)&&d(f,h))continue;const v=ih(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let u=e.slice();const _=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(_(s,e.a)&&_(i,e.b)||_(s,e.b)&&_(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>ih(t.a,t.b,r).localeCompare(ih(e.a,e.b,r)))){const e=[...t.targets][0],s=u.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;u=u.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(u=pt(u,t.a,t.b,e,i,r))}return u}const ah=t=>{const e=[];for(const s of t||[]){const t=at(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(h),d=new Map;for(const t of h){const e=kl("space",t);e.layers>0&&wl("space",e.root)&&xl(d,e.root,t)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=kl("room",t);o.layers>0&&wl("room",o.root)&&xl(s,o.root,t),u.has(t)||u.set(t,e),_.has(t)||_.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(u.keys()),f=new Map,b=new Map;for(const t of l)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=kl("space",t).root;if(!wl("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=kl("room",t).root;if(!wl("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of l){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),l=!0===t.removed;let h=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,h=i,p=!0,v.spaceRefsRemapped++):l||"virtual"===t.binding?l||(delete t.space,h=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,h=d.spaceId,v.spaceRefsRemapped++):(delete t.space,h=null,v.markersDetached++))}l&&!p||x(t,h,d),r&&p&&n===s&&h&&(o[e]={...a,s:h},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;$l(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),z=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},R=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),l=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(l)a=T(t,i,"device",String(l.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",_.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,z(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,R.add(i)}else delete o[t],z(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...R].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=Mt(n,a.layout);bt(n),Dt(a.layout);const l=JSON.stringify(t||{}),h=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&th(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(th(o,"segments")&&(delete o.segments,e++),th(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=Rt&&t<=At&&(th(o,"plan_scale_x")||(o.plan_scale_x=t),th(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);th(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?eh(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)th(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&th(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(th(t,"width")){const s=Number(t.width);(th(t,"width_cm")||Number.isFinite(s))&&(th(t,"width_cm")||(t.width_cm=Number(eh(s/Tt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(th(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),th(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(eh(Ft*Nt(t)/Tt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(th(t,"scale")||th(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Ht(t[1]));if(!(th(t,"entity")||th(t,"attr")||th(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":Lt(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const u=(_=n.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=Yr(n.spaces||[],a.layout);let g=0,f=0,v=0,y="";const $=m.spaces.map(t=>{const e=Ct(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/W*s;return i>v&&(v=i,y=String(t?.id||"")),e.space}),w=g?Yr($,m.layout):{...m,spaces:$};n.spaces=w.spaces;const k=g?{...m.report,moved:m.report.moved+w.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+w.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,w.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,w.report.maxShiftCm),maxSpace:w.report.maxShiftCm>m.report.maxShiftCm?w.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+w.report.rotated,removedDrafts:m.report.removedDrafts+w.report.removedDrafts}:{...m.report};let x=0,S=0,M=0,D=0,C=0,P=0,I=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=sh(e),r=sh(s);if(!a||!r)continue;const l=ah(a.rooms),h=ah(r.rooms),c=.02*Tt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:vt(t.a,t.b,W),cm:0})),p=new Set(d.map(t=>t.key)),_=Pt(e,a.rooms,b,c);S+=_.length;for(const t of _){const e=[t[0]/b,t[1]/b],s=[t[2]/b,t[3]/b],i=vt(e,s,W);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=It(d,l,h,W,b),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},v=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*b),s=t.b.map(t=>t*b);return h.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});M+=Math.max(0,m-v.length);const y=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...y,...v.map(({key:t,...e})=>e)];const $=v.map(t=>[t.a[0]*b,t.a[1]*b,t.b[0]*b,t.b[1]*b]),w=Array.isArray(e.walls)?e.walls.length:0;let k=It(e.walls,l,h,W,b);k=oh(r.rooms,k,$,W,Number(s.cell_cm)>0?Number(s.cell_cm):5,Tt,b),k=ut(r.rooms,k,$,W,Number(s.cell_cm)>0?Number(s.cell_cm):5,Tt,b),k=zt(k,s.rooms||[],W,1,$.map(t=>[t[0]/b,t[1]/b,t[2]/b,t[3]/b])),x+=Math.max(0,w-k.length),k.length?s.walls=k:delete s.walls;const z=il(s.partitions||[],{pitch:W,geometry:al(s)});z.merged&&(D+=z.merged,s.partitions=z.partitions,ol(s.openings,s.partitions,z.openingMoves,{coordScale:b,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:Tt}));const R=sh(s);if(R){const t=o(s,R,s.walls||[],$,{pitch:W,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:Tt,coordScale:b});t.partitionsReconciled&&(C+=t.partitionsReconciled,P+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(I+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let z=0;c<=9&&(z=function(t){const e=Xl(t);return Ql(t,e.config),{...e,config:t}}(n).migratedSegments);const R=bt(n),A=Dt(w.layout),F=JSON.stringify(R)!==l||JSON.stringify(A)!==h;c<9&&F&&(R.model_version=9);const N=JSON.stringify(R)!==l||JSON.stringify(A)!==h,H=Number.isInteger(Number(R.model_version))?Number(R.model_version):c,L=N?k:{...k,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},E=N?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:R,layout:A,report:{...L,modelFrom:c,modelTo:H,migrated:N?p:0,glowSpacesMigrated:N?d.glowSpaces:0,glowRoomsMigrated:N?d.glowRooms:0,canonicalized:N?T:0,wallSegmentsMigrated:N?z:0,legacyZeroWallsMigrated:N?S:0,wallsMerged:N?x:0,spansMerged:N?M:0,partitionsMerged:N?D:0,partitionsReconciled:N?C:0,openingsRehosted:N?P:0,redundantDraftsRemoved:N?I:0,wallsStraightened:N?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:N?v:0,maxStraightenSpace:N?y:"",latticeCoordinatesCanonicalized:N?r.canonicalized:0,latticeCoordinatesFar:N?r.far:0,latticeMaxShift:N?r.maxShift:0,latticeMaxShiftCm:N?r.maxShiftCm:0,latticeSpaces:N?r.spaces:[],...E},changed:N}}const rh=new WeakSet,lh=t=>{t._regSignature="",t._maybeRebuildDevices(),t.requestUpdate()};async function hh(t,e,s,i,o,a=""){const n=t.host,r=n._serverCfg;if(!r)return!1;let l=JSON.parse(JSON.stringify(r));l.markers=l.markers||[];let h=l.markers.find(t=>t.id===e);if(!h){const t=n._devices.find(t=>t.id===e);if(!t||"device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return!1;h={id:t.id,binding:`${t.bindingKind}:${t.bindingRef}`,space:t.space||null,area:t.area||null,hidden:!!t.hidden},l.markers.push(h)}h.vacuum=function(t,e){const s=e.matrix.map(t=>Number(t.toFixed(6))),i=t?.map_routes;if(Array.isArray(i)&&i.length){const o=i.find(t=>t.id===e.routeId)||i.find(t=>t.source===e.source&&t.map_id===e.mapId);if(o)return{...t,map_routes:Do(i,o.id,s)}}return{...t,source:e.source,calibration:{...t?.calibration||{},[e.mapId]:s}}}(h.vacuum||{},{source:s,mapId:i,routeId:a,matrix:o}),l=t._prepareConfigCandidate(l);const c=u(r,l,n._cfgContentFingerprint,n._cfgRev,_);n._serverCfg=l,lh(n),n._saveConfigDebounced.pending()&&n._saveConfigDebounced.cancel();try{return await t._saveConfigNow(c),!0}catch(t){return m(n,c,_),lh(n),n._showToast(n._t("toast.cfg_save_failed",{err:n._errText(t)})),!1}}function ch(t){return 1===t}function dh(t){return`${t}-${globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`}function ph(t){const e=function(t){return JSON.stringify({message:t.message.trim(),contact:t.contact.trim(),previewToken:t.attach&&t.preview?t.preview.token:""})}(t);return{idempotencyKey:t.submissionFingerprint&&t.submissionFingerprint!==e?dh("report"):t.idempotencyKey,fingerprint:e}}function uh(t){return[...t].length}function _h(t,e=Date.now()){const s=t.message.trim();return s?uh(s)>1e4?"message_too_long":uh(t.contact.trim())>320?"contact_too_long":t.attach&&!t.preview?"preview_missing":t.attach&&t.preview.expiresAt<=e?"preview_expired":null:"message_required"}function mh(t,e=Date.now()){return"building"!==t.status&&"sending"!==t.status&&"success"!==t.status&&null===_h(t,e)}function gh(t){const e=function(t){const e=/Firefox\/(\d+)/i.exec(t);if(e)return{family:"firefox",major:Number(e[1])||0};const s=/(?:Chrome|Chromium|Edg)\/(\d+)/i.exec(t);if(s)return{family:"chromium",major:Number(s[1])||0};const i=/Version\/(\d+).+Safari\//i.exec(t);return i?{family:"webkit",major:Number(i[1])||0}:{family:"unknown",major:0}}(t.userAgent??globalThis.navigator?.userAgent??""),s=t.now??Date.now(),i=Number(t.registryLastSuccess||0),o=i<=0?"unknown":s-i<=6e5?"fresh":"stale",a=["en","ru","de","fr"].includes(t.language)?t.language:"en",n=globalThis.matchMedia?.bind(globalThis);return{browser_family:e.family,browser_major:Math.max(0,Math.min(999,e.major)),language:a,coarse_pointer:t.coarsePointer??!!n?.("(pointer: coarse)").matches,hover_capable:t.hoverCapable??!!n?.("(hover: hover)").matches,registry_access:"full"===t.registryAccess?"full":"limited"===t.registryAccess?"partial":"unavailable",registry_age_bucket:o}}function fh(t){const e=t&&"object"==typeof t&&"code"in t?String(t.code||""):"";return new Set(["support_invalid_message","support_preview_expired","support_package_too_large","support_rate_limited","support_unavailable","support_rejected","unauthorized"]).has(e)?e:"support_unavailable"}const bh=[{id:"air_conditioner",group:"appliance",art:{d:"M20.5 39h69a2.5 2.5 0 0 1 2.5 2.5V69a2.5 2.5 0 0 1-2.5 2.5h-69A2.5 2.5 0 0 1 18 69V41.5a2.5 2.5 0 0 1 2.5-2.5 M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v8.5H25z M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v3.5H25z",viewW:110,viewH:110}},{id:"armchair",group:"furniture",art:{d:"M42.5 29h25a10 10 0 0 1 10 10v26.5a10 10 0 0 1-10 10h-25a10 10 0 0 1-10-10V39a10 10 0 0 1 10-10 M77.5 48.5a6 6 0 0 1 2 11.657V71.5a4 4 0 0 1-4 4H34a4 4 0 0 1-4-4V60.157A6.001 6.001 0 0 1 32 48.5a6 6 0 0 1 5.419 3.424A4 4 0 0 1 38 54v7h33.5v-6.5q0-.107.005-.213A6 6 0 0 1 77.5 48.5 M42.75 57h24.5A4.75 4.75 0 0 1 72 61.75a4.75 4.75 0 0 1-4.75 4.75h-24.5A4.75 4.75 0 0 1 38 61.75 4.75 4.75 0 0 1 42.75 57M36.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 41.392 82H38a1.5 1.5 0 0 1-1.5-1.5zm36 0h-8l1.674 5.441A1.5 1.5 0 0 0 67.608 82H71a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"bathtub",group:"sanitary",art:{d:"M28 75h8l-1.674 5.441a1.5 1.5 0 0 1-1.434 1.059H29.5A1.5 1.5 0 0 1 28 80zm53 0h-8l1.674 5.441a1.5 1.5 0 0 0 1.434 1.059H79.5A1.5 1.5 0 0 0 81 80zM19.5 46.5H90a2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5H19.5A2.5 2.5 0 0 1 17 49a2.5 2.5 0 0 1 2.5-2.5 M19.5 51.5H90L86.633 68a10 10 0 0 1-9.798 8h-44.17a10 10 0 0 1-9.798-8zM30 36v-2a5 5 0 1 0-10 0v12.5",viewW:110,viewH:110}},{id:"bed",group:"furniture",art:{d:"M16 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 20.892 82H17.5a1.5 1.5 0 0 1-1.5-1.5zm78.5 0h-8l1.674 5.441A1.5 1.5 0 0 0 89.608 82H93a1.5 1.5 0 0 0 1.5-1.5zM25 29h60.5a2.5 2.5 0 0 1 2.5 2.5V68a2.5 2.5 0 0 1-2.5 2.5H25a2.5 2.5 0 0 1-2.5-2.5V31.5A2.5 2.5 0 0 1 25 29 M16 59.5c0-5.523 4.477-10 10-10h58.5c5.523 0 10 4.477 10 10v6H16zm-1 6h80.5a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H15a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1m15.5-20a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4h-21zm28.5 0a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4H59z",viewW:110,viewH:110}},{id:"bidet",group:"sanitary",art:{d:"M36 57.5c0 5.523 4.477 10 10 10h18.5c5.523 0 10-4.477 10-10V53H36zm8.5 22a1 1 0 0 0 1 1H65a1 1 0 0 0 1-1v-12H44.5zm-8.5-32a1 1 0 0 1 1-1h36.5a1 1 0 0 1 1 1V53H36zM50.5 41a1 1 0 0 1 1-1H59a1 1 0 0 1 1 1v5.5h-9.5zM53 30a1 1 0 0 1 1-1h2.5a1 1 0 0 1 1 1v10H53zm-4 6a1 1 0 0 1-1-1v-2.5a1 1 0 0 1 1-1h4V36z",viewW:110,viewH:110}},{id:"boiler",group:"appliance",art:{d:"M46.5 81.5h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zm12.5 0h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zM44 23h22.5a8 8 0 0 1 8 8v42.5a8 8 0 0 1-8 8H44a8 8 0 0 1-8-8V31a8 8 0 0 1 8-8 M36 31a8 8 0 0 1 8-8h22.5a8 8 0 0 1 8 8v42H36z M48.5 42.5a7 7 0 1 0 14 0 7 7 0 1 0-14 0m6.5 0 3.5-3.5",viewW:110,viewH:110}},{id:"chair",group:"furniture",art:{d:"M40.5 39.5C40.5 32.596 46.096 27 53 27h4c6.904 0 12.5 5.596 12.5 12.5v13a5 5 0 0 1-5 5h-19a5 5 0 0 1-5-5z M41.25 54h27.5A3.25 3.25 0 0 1 72 57.25a3.25 3.25 0 0 1-3.25 3.25h-27.5A3.25 3.25 0 0 1 38 57.25 3.25 3.25 0 0 1 41.25 54m28.25 6.5H64l1.88 21.63a1.5 1.5 0 0 0 1.495 1.37H68a1.5 1.5 0 0 0 1.5-1.5zm-29 0H46l-1.88 21.63a1.5 1.5 0 0 1-1.495 1.37H42a1.5 1.5 0 0 1-1.5-1.5z",viewW:110,viewH:110}},{id:"coffee_table",group:"furniture",art:{d:"M25 51h5.238l-1.534 15.646A1.5 1.5 0 0 1 27.211 68H26.5a1.5 1.5 0 0 1-1.5-1.5zm60.238 0H80l1.534 15.646A1.5 1.5 0 0 0 83.027 68h.711a1.5 1.5 0 0 0 1.5-1.5z M24.34 43.485A2.5 2.5 0 0 1 26.625 42h56.75a2.5 2.5 0 0 1 2.285 1.485l2.715 6.109A1 1 0 0 1 87.461 51H22.54a1 1 0 0 1-.914-1.406z M87.5 51h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"computer",group:"appliance",art:{d:"M24.5 38h40a2.5 2.5 0 0 1 2.5 2.5v22a2.5 2.5 0 0 1-2.5 2.5h-40a2.5 2.5 0 0 1-2.5-2.5v-22a2.5 2.5 0 0 1 2.5-2.5m50 0h11a2.5 2.5 0 0 1 2.5 2.5v29a2.5 2.5 0 0 1-2.5 2.5h-11a2.5 2.5 0 0 1-2.5-2.5v-29a2.5 2.5 0 0 1 2.5-2.5M43 65h5v7h-5zm-6 7h16m23-12h8m-8-17h8 M35 56V46h5m-5 7h5 M39.5 53a3.5 3.5 0 1 0 0-7m15.036 8.536a5 5 0 1 1 0-7.071",viewW:110,viewH:110}},{id:"cooktop",group:"appliance",art:{d:"M27.5 29h55a2.5 2.5 0 0 1 2.5 2.5v47a2.5 2.5 0 0 1-2.5 2.5h-55a2.5 2.5 0 0 1-2.5-2.5v-47a2.5 2.5 0 0 1 2.5-2.5 M33.5 66.5a8 8 0 1 0 16 0 8 8 0 1 0-16 0m26.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0m3 23a5 5 0 1 0 10 0 5 5 0 1 0-10 0m-29.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0",viewW:110,viewH:110}},{id:"dining_table",group:"furniture",art:{d:"M83.5 43H78l6.742 30.82A1.5 1.5 0 0 0 86.207 75h.54a1.5 1.5 0 0 0 1.482-1.732zm-56 0H33l-6.742 30.82A1.5 1.5 0 0 1 24.793 75h-.54a1.5 1.5 0 0 1-1.482-1.732zm-2.62-7.406A2.5 2.5 0 0 1 27.21 34h56.58a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044A1 1 0 0 1 87.538 43H23.462a1 1 0 0 1-.932-1.362z M88.5 43h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"dishwasher",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5M43 42h24m-1.5-11a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"dryer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5 M55.994 55.5s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m5.979-11s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m-8.519-11s-.886 1.81-.986 3.143c-.132 1.761 1.066 2.554.986 4.321-.067 1.479-.986 3.536-.986 3.536",viewW:110,viewH:110}},{id:"exercise",group:"other",art:{d:"m60.752 62.03-5.379-2.312 11.066-25.75a1.5 1.5 0 0 1 1.97-.786l2.624 1.127a1.5 1.5 0 0 1 .786 1.97zm-18.648-4.512-4.353 1.189-4.474-16.382a1.5 1.5 0 0 1 1.052-1.842l1.459-.398a1.5 1.5 0 0 1 1.842 1.052z M41.297 38.173a2.215 2.215 0 0 1 0 4.43H29.781a2.215 2.215 0 0 1 0-4.43zm33.219 38.091a2.657 2.657 0 1 1 0 5.315H28.453a2.657 2.657 0 1 1 0-5.315zm8.641-51.366a1.795 1.795 0 0 1 .68 2.41l-3.03 5.59a5 5 0 0 1-4.396 2.618H65.657a1.772 1.772 0 1 1 0-3.544h9.61a3 3 0 0 0 2.598-1.5l2.84-4.917a1.795 1.795 0 0 1 2.452-.657M29.339 65.787c0-6.699 6.457-11.503 12.873-9.579l24.291 7.288a10 10 0 0 1 7.127 9.578v3.19H29.339zm0 15.852h7.086l-1.394 3.486a1.5 1.5 0 0 1-1.393.943h-2.8a1.5 1.5 0 0 1-1.5-1.5zm44.291 0h-7.087l1.395 3.486a1.5 1.5 0 0 0 1.392.943h2.8a1.5 1.5 0 0 0 1.5-1.5z M35.54 66.077a4.872 4.872 0 1 0 9.743 0 4.872 4.872 0 1 0-9.744 0",viewW:110,viewH:110}},{id:"fireplace",group:"other",art:{d:"M26.672 34.425h55.99v39.47h-55.99Z M23 74.894a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v5.802a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zM23 29a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v4.425a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zm15.145 21.44a5 5 0 0 1 5-5h23.043a5 5 0 0 1 5 5v23.454H38.145z M44.57 65.633a2.754 2.754 0 0 1 2.754-2.753H62.01v5.507H47.324a2.754 2.754 0 0 1-2.754-2.754m20.194 5.507a2.754 2.754 0 0 0-2.754-2.753H47.324v5.507H62.01a2.754 2.754 0 0 0 2.754-2.754 M44.57 71.14a2.754 2.754 0 1 0 5.508 0 2.754 2.754 0 1 0-5.508 0m14.688-5.507a2.754 2.754 0 1 0 5.507 0 2.754 2.754 0 1 0-5.507 0",viewW:110,viewH:110}},{id:"fridge",group:"appliance",art:{d:"M35.5 18H75a2.5 2.5 0 0 1 2.5 2.5v64A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5v-64a2.5 2.5 0 0 1 2.5-2.5 M33 46.5h44.5v38A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5zm2.5 44a1 1 0 0 0 1 1h37a1 1 0 0 0 1-1V87h-39zm3-49.5v-9.5m0 30.5v-9.5",viewW:110,viewH:110}},{id:"hood",group:"appliance",art:{d:"M44.308 57h21.384L83 69H27zM44 37.5a2.5 2.5 0 0 1 2.5-2.5h17a2.5 2.5 0 0 1 2.5 2.5V57H44zM80.5 74h-51a2.5 2.5 0 0 1-2.5-2.5V70a1 1 0 0 1 1-1h54a1 1 0 0 1 1 1v1.5a2.5 2.5 0 0 1-2.5 2.5",viewW:110,viewH:110}},{id:"kitchen_cabinet",group:"furniture",art:{d:"M38 88a1 1 0 0 0 1 1h32a1 1 0 0 0 1-1v-3H38zm17-68h16.5a2.5 2.5 0 0 1 2.5 2.5v17a2.5 2.5 0 0 1-2.5 2.5H55zm-19 2.5a2.5 2.5 0 0 1 2.5-2.5H55v22H38.5a2.5 2.5 0 0 1-2.5-2.5zM59 33v5m-8-5v5m4 47h16.5a2.5 2.5 0 0 0 2.5-2.5v-17a2.5 2.5 0 0 0-2.5-2.5H55zm-19-2.5a2.5 2.5 0 0 0 2.5 2.5H55V63H38.5a2.5 2.5 0 0 0-2.5 2.5zM67 67h-5m-14 0h-5",viewW:110,viewH:110}},{id:"kitchen_sink",group:"sanitary",art:{d:"M21.5 84h68a2.5 2.5 0 0 0 2.5-2.5v-35a2.5 2.5 0 0 0-2.5-2.5h-68a2.5 2.5 0 0 0-2.5 2.5v35a2.5 2.5 0 0 0 2.5 2.5 M26 78h26.5a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H26a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m23-24v5m9.5 19H85a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H58.5a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m-6-39.5a1 1 0 0 0-1-1H44a1 1 0 0 0-1 1V44h9.5zM58 33v-2a5 5 0 1 0-10 0v6.5M62 54v5",viewW:110,viewH:110}},{id:"nightstand",group:"furniture",art:{d:"M76.5 73.5h-8l1.674 5.441A1.5 1.5 0 0 0 71.608 80H75a1.5 1.5 0 0 0 1.5-1.5zm-42.5 0h8l-1.674 5.441A1.5 1.5 0 0 1 38.892 80H35.5a1.5 1.5 0 0 1-1.5-1.5z M34.5 30.5h41A2.5 2.5 0 0 1 78 33v40.5a2.5 2.5 0 0 1-2.5 2.5h-41a2.5 2.5 0 0 1-2.5-2.5V33a2.5 2.5 0 0 1 2.5-2.5 M32 39.5h46v18H32Zm18.5 9H60M50.5 66H60",viewW:110,viewH:110}},{id:"oven",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5 M40.5 47h29a2.5 2.5 0 0 1 2.5 2.5v20a2.5 2.5 0 0 1-2.5 2.5h-29a2.5 2.5 0 0 1-2.5-2.5v-20a2.5 2.5 0 0 1 2.5-2.5m25-16a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5M43 42h24",viewW:110,viewH:110}},{id:"plant",group:"other",art:{d:"M45.952 75.849a2.5 2.5 0 0 0 2.485 2.231H63.43a2.5 2.5 0 0 0 2.485-2.231l1.571-14.532H44.381zM60.236 44.78C58.423 36.174 66.805 29.605 77 31.87c0 8.155-8.609 16.763-16.763 12.912m-9.761 5.147c1.517-8.665-7.085-14.943-17.195-12.33.28 8.15 9.178 16.458 17.195 12.33 M55.706 55.88v-2.944m0 0c1.133-5.89 7.09-10.956 12.912-15.404M55.706 52.936c-4.938-4.146-7.982-6.16-14.044-9.061m1.453 12.005h25.636a1 1 0 0 1 1 1v3.437a1 1 0 0 1-1 1H43.115a1 1 0 0 1-1-1v-3.436a1 1 0 0 1 1-1",viewW:110,viewH:110}},{id:"rug",group:"other",art:{d:"M90.27 33.874h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.154h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20M28.12 32h54.025a2.5 2.5 0 0 1 2.5 2.5v40.439a2.5 2.5 0 0 1-2.5 2.5H28.12a2.5 2.5 0 0 1-2.5-2.5V34.5a2.5 2.5 0 0 1 2.5-2.5 M25.62 34.5a2.5 2.5 0 0 1 2.5-2.5h1.717v45.439H28.12a2.5 2.5 0 0 1-2.5-2.5zm59.026 0a2.5 2.5 0 0 0-2.5-2.5H80.43v45.439h1.716a2.5 2.5 0 0 0 2.5-2.5zM37.8 54.485l17.801-8.432 17.801 8.432-17.8 8.432z",viewW:110,viewH:110}},{id:"shelving",group:"furniture",art:{d:"M28 58.5h53V39H28Z M64 58.5h5V47a1 1 0 0 0-1-1h-3a1 1 0 0 0-1 1zM52.5 39h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-12 0h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-5 29.5V71a1 1 0 0 1 1 1H49a1 1 0 0 1 1-1v-2.5a1 1 0 0 1-1-1H36.5a1 1 0 0 1-1 1m33.5-10h7v-14a1 1 0 0 0-1-1h-5a1 1 0 0 0-1 1zM47.5 39h-7V25a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1zm-12 34v4a1 1 0 0 1 1 1h14a1 1 0 0 1 1-1v-4a1 1 0 0 1-1-1h-14a1 1 0 0 1-1 1M28 78h53v7H28Z M28 89.5V23a2.5 2.5 0 0 1 2.5-2.5h48A2.5 2.5 0 0 1 81 23v66.5",viewW:110,viewH:110}},{id:"shower",group:"sanitary",art:{d:"M31 84h49a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H31a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1m31.5-47.6v-4.686a11.7 11.7 0 0 0-3.441-8.283A11.77 11.77 0 0 0 50.75 20a11.77 11.77 0 0 0-8.309 3.431A11.7 11.7 0 0 0 39 31.714V84 M72 39.25a10.25 10.25 0 1 0-20.5 0zm-17.25 5v2.5m7.25-2.5v2.5m7.25-2.5v2.5m-14.5 3.75V53M62 50.5V53m7.25-2.5V53m-14.5 3.75v2.5m7.25-2.5v2.5m7.25-2.5v2.5M54.75 63v2.5M62 63v2.5m7.25-2.5v2.5",viewW:110,viewH:110}},{id:"sink",group:"sanitary",art:{d:"M30 57c0 5.523 4.477 10 10 10h29.5c5.523 0 10-4.477 10-10v-6.5A2.5 2.5 0 0 0 77 48H32.5a2.5 2.5 0 0 0-2.5 2.5zm14 22a1 1 0 0 0 1 1h19.5a1 1 0 0 0 1-1V67H44zm6-36.5a1 1 0 0 1 1-1h7.5a1 1 0 0 1 1 1V48H50zM45 37v-2a5 5 0 1 1 10 0v6.5",viewW:110,viewH:110}},{id:"sofa",group:"furniture",art:{d:"M59.5 29h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5m-34 0h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5 M18.5 44a5 5 0 0 1 5 5v12H86V49a5 5 0 0 1 5-5h.5a5 5 0 0 1 5 5v21.5a5 5 0 0 1-5 5H18a5 5 0 0 1-5-5V49a5 5 0 0 1 5-5z M25.25 57h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 25.25 57m34 0h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 59.25 57M18.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 23.392 82H20a1.5 1.5 0 0 1-1.5-1.5zm72 0h-8l1.674 5.441A1.5 1.5 0 0 0 85.608 82H89a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"stairs",group:"other",art:{d:"M73.47 46.365v-17.62m-10.571 25.55V36.674M52.326 61.783V45.484M42.193 69.273v-15.86M28.979 77.761a1 1 0 0 1 1-1h53.624v6.489a1 1 0 0 1-1 1H29.98a1 1 0 0 1-1-1z M26.777 57.497a1 1 0 0 1 1-1h2.846a1 1 0 0 1 1 1V83.25a1 1 0 0 1-1 1h-2.846a1 1 0 0 1-1-1z m73.113 28.633-41.492 32.93v-4.405l39.9-32.007a.7.7 0 0 1 .428-.151h3.724a1.762 1.762 0 1 1 0 3.524h-2.248a.5.5 0 0 0-.312.109M25.895 53.414a3.084 3.084 0 1 0 6.167 0 3.084 3.084 0 1 0-6.167 0m14.978 16.859a1 1 0 0 1 1-1h41.73v7.488h-42.73zm10.131-7.489a1 1 0 0 1 1-1h31.598v7.488H51.004z M61.576 55.294a1 1 0 0 1 1-1h21.026v7.49H61.576z M72.15 47.806a1 1 0 0 1 1-1h9.454a1 1 0 0 1 1 1v6.489H72.15z",viewW:110,viewH:110}},{id:"toilet",group:"sanitary",art:{d:"M32 26.5a2.5 2.5 0 0 1 2.5-2.5H45a2.5 2.5 0 0 1 2.5 2.5V57H32zM47.5 52h29a2.5 2.5 0 0 1 2.5 2.5V57H47.5zm17.037 20.102C73.95 70.288 79 62.01 79 57H32c.5 7.5 8.072 7 7 11l-2.342 16.862a1 1 0 0 0 .99 1.138H66.72a1 1 0 0 0 .97-1.243z",viewW:110,viewH:110}},{id:"tv",group:"appliance",art:{d:"M23.5 31H86a2.5 2.5 0 0 1 2.5 2.5v34A2.5 2.5 0 0 1 86 70H23.5a2.5 2.5 0 0 1-2.5-2.5v-34a2.5 2.5 0 0 1 2.5-2.5M50 70h9.5v8H50Zm-10 8h30M42.5 45.5h5m5 0h-5m0 0v9.75 m56.75 45.5 5 10 5-10",viewW:110,viewH:110}},{id:"wardrobe",group:"furniture",art:{d:"M28 23a2.5 2.5 0 0 1 2.5-2.5h24V85h-24a2.5 2.5 0 0 1-2.5-2.5zm2 65.5a1 1 0 0 0 1 1h47a1 1 0 0 0 1-1V85H30zM81 23a2.5 2.5 0 0 0-2.5-2.5h-24V85h24a2.5 2.5 0 0 0 2.5-2.5zM50.5 48v9.5m8-9.5v9.5",viewW:110,viewH:110}},{id:"washer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"work_table",group:"furniture",art:{d:"M88.5 47h-20v14h20zm-12 4h4m7.984-8h-66v4h66zm-66 4h4v26.5a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5z M24.866 35.597A2.5 2.5 0 0 1 27.197 34h56.577a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044a1 1 0 0 1-.93 1.362H23.46a1 1 0 0 1-.933-1.361zM88.5 61h-20v12.5A1.5 1.5 0 0 0 70 75h17a1.5 1.5 0 0 0 1.5-1.5zm-12 4h4",viewW:110,viewH:110}}];class vh{constructor(t,e){this.host=t,this.hooks=e}place(t,e="mouse"){const s=this.host._decorImagePalette,i=this.host._curSpaceCfg;if(!s||!i)return;const o=this.hooks.decorSnap(t,e),a=Ot(s.width,s.height),n=Bt(a.w,this.host._cellCm,this.host._gridPitch)/b,r=Bt(a.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,l="di"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),h=this.hooks.geometrySnapshot();i.decor=[...this.host._decorList,{id:l,kind:"image",asset_id:s.asset_id,x:G(o[0]/b-n/2),y:G(o[1]/this.host._decorH-r/2),w:n,h:r,opacity:1}],this.host._decorSel=l,this.host._decorTool="select",this.host._decorImagePalette=null,this.hooks.clearFurniturePreview(),this.hooks.recordGeometry(this.host._t("history.decor_add"),h),this.hooks.saveConfig(),this.host.requestUpdate()}renderPlacementPreview(){const t=this.host._furnPreviewInput,e=this.host._decorImagePalette;if(!t||!e||"decor"!==this.host._mode||"image"!==this.host._decorTool||!this.host._pointerModality.hoverEnabled)return d;const s=this.hooks.decorSnap(t.raw,"mouse"),i=Ot(e.width,e.height),o=Bt(i.w,this.host._cellCm,this.host._gridPitch)/b,a=Bt(i.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,n=this.host._display(e.url);return n?f``:d}renderMissing(t,e,s,i,o,a,n,r,l){const h=i+a/2,c=o+n/2;return f` + ${this.host._t("decor.image_unavailable")} + + + ${this.host._t("decor.image_unavailable")} + `}async uploadFromInput(t,e=!1){const s=t.target,i=s.files?.[0];if(s.value="",!i||this.host._decorAssetBusy)return;let o;try{o=await no(i,2097152)}catch{return void this.host._showToast(this.host._t("backup.error.invalid_image"))}if("reject"!==o.kind)return"guard"===o.kind?(this.hooks.setGuardReplace(e),void(this.host._backdropGuard=o.state)):void await this.upload(i,i.name,e);this.host._showToast(this.host._t("toast.plan_formats"))}async upload(t,e,s){if(!this.host._decorAssetBusy){this.host._decorAssetBusy=!0;try{const i=new FormData;i.append("file",t,e);const o=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/assets/upload",{method:"POST",body:i}):await fetch("/api/houseplan/assets/upload",{method:"POST",body:i,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await o.json().catch(()=>({}));if(!o.ok||a.error||!a.asset){const t={invalid_format:"backup.error.invalid_image",invalid_image:"backup.error.invalid_image",unsupported_image:"backup.error.unsupported_image",too_large:"backdrop.too_large_title",capacity_exceeded:"decor.image_error_capacity"};throw new Error(this.host._t(t[String(a.error)]||"backup.error.io_error"))}const n=Wt({assets:[a.asset]}).values().next().value;if(!n)throw new Error(this.host._t("backup.error.io_error"));this.host._decorAssets=new Map(this.host._decorAssets).set(n.asset_id,n),this.host._decorAssetCatalog=[n,...this.host._decorAssetCatalog.filter(t=>t.asset_id!==n.asset_id)],s&&"image"===this.host._decorShapeDialog?.kind?this.host._decorShapeDialog={...this.host._decorShapeDialog,assetId:n.asset_id}:this.host._decorImagePalette=n,this.host.requestUpdate()}catch(t){this.host._showToast(this.host._t("decor.image_upload_failed",{err:this.host._errText(t)}))}finally{this.host._decorAssetBusy=!1}}}async delete(t){if(t.used_by?.length)return void this.host._showToast(this.host._t("decor.image_in_use"));if(await this.host._confirmDanger({key:`delete-decor-asset:${t.asset_id}`,kind:"destructive",title:this.host._t("decor.image_delete_title"),message:this.host._t("decor.image_delete_message",{name:t.name}),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}))try{await this.host.hass.callWS({type:"houseplan/assets/delete",asset_id:t.asset_id}),this.host._decorAssetCatalog=this.host._decorAssetCatalog.filter(e=>e.asset_id!==t.asset_id),this.host._decorAssets.delete(t.asset_id),this.host._decorImagePalette?.asset_id===t.asset_id&&(this.host._decorImagePalette=null),this.host.requestUpdate()}catch(t){this.host._showToast("in_use"===t?.code?this.host._t("decor.image_in_use"):this.host._t("backup.error.io_error"))}}async loadCatalog(){try{const t=await this.host.hass.callWS({type:"houseplan/assets/list"}),e=Wt(t);this.host._decorAssetCatalog=[...e.values()],this.host._decorAssets=new Map([...this.host._decorAssets,...e]),this.host.requestUpdate()}catch{this.host._showToast(this.host._t("backup.error.io_error"))}}renderImagePalette(){const t=this.host._decorImagePalette;return p``}renderFurniturePalette(){const t=this.host._furnPalette,e=this.host._furnCategory,s=jt.flatMap(t=>Gt(t)),i=bh.filter(t=>s.some(e=>e.category===t.id)),o=i.find(t=>t.id===e)||null,a=o?s.filter(t=>t.category===o.id):[],n=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`
    t.stopPropagation()}> +
    + ${this.host._t("furn.title")} + + +
    +
    + ${o?p` + +
    + ${this.host._t(`furn.cat_${o.id}`)} +
    +
    + ${a.map(e=>p``)} +
    + `:jt.map(t=>p` +
    ${this.host._t(`furn.group_${t}`)}
    +
    + ${i.filter(e=>e.group===t).map(t=>p``)} +
    `)} +
    + ${t?p`
    + + this.host._furnPalette={...t,w:this.hooks.furnFieldToCm(Number(e.target.value))}} /> + + this.host._furnPalette={...t,h:this.hooks.furnFieldToCm(Number(e.target.value))}} /> + ${this.host._t("furn.place_hint")} +
    `:p`
    ${this.host._t("furn.pick_hint")}
    `} +
    `}furnitureSizeInput(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i=qt(e,this.host._imperial,Ut*this.host._cellCm),o="w"===t?"sizeWField":"sizeHField",a="w"===t?"sizeWCm":"sizeHCm",n="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[o]:e,...null===i?{}:{[a]:Math.abs(i),[n]:i<0}}}furnitureFlip(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i="w"===t?s.sizeWField:s.sizeHField,o=qt(i,this.host._imperial,Ut*this.host._cellCm),a="w"===t?s.sizeWCm:s.sizeHCm,n=Math.abs(o??a??.1),r="w"===t?"sizeWField":"sizeHField",l="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[r]:Kt(n,e,this.host._imperial),[l]:e}}renderShapeDialog(){const t=this.host._decorShapeDialog,e="rect"===t.kind||"ellipse"===t.kind,s=this.host._t(`decor.${t.kind}`),i=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m"),o="furniture"===t.kind||"image"===t.kind?qt(t.sizeWField,this.host._imperial,Ut*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?qt(t.sizeHField,this.host._imperial,Ut*this.host._cellCm):null,n=!("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a),r="image"===t.kind?this.host._decorAssetCatalog.find(e=>e.asset_id===t.assetId)||this.host._decorAssets.get(t.assetId||""):null;return p`this.host._decorShapeDialog=null}> +
    + ${"furniture"===t.kind?p` + + `:"image"===t.kind?p` + + + ${r?p`
    + ${r.name} + ${r.name} +
    `:d} + `:d} + ${"image"===t.kind?p` + + this.host._decorShapeDialog={...t,opacity:Number(e.target.value)}} />`:p`this.host._decorShapeDialog={...t,...e.detail}}>`} + ${"image"!==t.kind?p` +
    this.host._decorShapeDialog={...t,widthCm:this.host._decorSmallCm(Number(e.target.value))}} />${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    `:d} + ${"line"===t.kind?p` + +
    + + +
    + +
    this.host._decorShapeDialog={...t,lengthCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ${i}
    `:"furniture"===t.kind||"image"===t.kind?p` + +
    this.furnitureSizeInput("w",t.target.value)} /> + ×this.furnitureSizeInput("h",t.target.value)} /> + ${i}
    + + `:p` + +
    this.host._decorShapeDialog={...t,sizeWCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ×this.host._decorShapeDialog={...t,sizeHCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ${i}
    `} + + this.host._decorShapeDialog={...t,angle:e.target.value}} /> + ${e?p` + this.host._decorShapeDialog={...t,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}>`:d} +
    +
    + + + +
    +
    `}}const yh=(t,e)=>t[0]*e[1]-t[1]*e[0],$h=(t,e)=>t[0]*e[0]+t[1]*e[1],wh=(t,e)=>[t[0]-e[0],t[1]-e[1]],kh=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function xh(t,e,s){return Math.abs(yh(t,e))<=s}function Sh(t,e,s){return Math.abs(yh(wh(t,e),s))}function Mh(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const l=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&xh(l,[i/n,a/n],1e-6)))return[];if(Sh(e.a,t,l)>1.1*Math.max(s,o)||Sh(e.b,t,l)>1.1*Math.max(s,o))return[];return[[$h(wh(e.a,t),l),$h(wh(e.b,t),l)]]}),o).flatMap(([e,i])=>{const o=Yt({a:kh(t,l,e),b:kh(t,l,i),halfDepth:s});return o?[o]:[]})}function Dh(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=K(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=Vt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:kh([t.x,t.y],s,-i),b:kh([t.x,t.y],s,i)}}),a=[];for(const i of Jt(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Mh(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=x(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Mh(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function Ch(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=kh(s.center,a,o),r=[];for(let e=0;ei&&xh(s.axis,[l[0]/h,l[1]/h],1e-6)))continue;if(Sh(o,n,s.axis)>i||Sh(a,n,s.axis)>i)continue;const c=$h(wh(o,n),s.axis),d=$h(wh(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const l=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!l)return null;const[h,c]=l,d=kh(n,s.axis,-s.half),p=kh(n,s.axis,s.half),u=Math.max(0,-s.half-h),_=Math.max(0,c-s.half),m=u>0?kh(n,s.axis,h):d,g=_>0?kh(n,s.axis,c):p,f=$h(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,u),right:b(p,g,_)}}function Ph(t){const e=kh(t.center,t.axis,-t.half),s=kh(t.center,t.axis,t.half),i=kh(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=kh(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Ih(t,e,s,i){const o=wh(s,e),a=$h(o,o);if(!(a>i*i))return Math.hypot(...wh(t,e))<=i;const n=$h(wh(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=kh(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...wh(t,r))<=i}function Th(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=h>t[1]&&t[0]<(l-n)*(t[1]-r)/(h-r)+n&&(i=!i)}return i}function zh(t,e,s,i,o,a){const n=wh(i,s),r=yh(e,n);if(Math.abs(r)<=a)return null;const l=wh(s,t),h=yh(l,n)/r,c=yh(l,e)/r;return h<-a||h>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,h))}function Rh(t,e,s){const i=Ph(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Th(t,r.body,a))return 0;for(let i=0;i$h(wh(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=wh(n,a),l=Math.hypot(r[0],r[1]);if(!(l>s&&xh(e.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(Sh(a,e.center,e.axis)>s||Sh(n,e.center,e.axis)>s)continue;const h=$h(wh(a,e.center),e.axis),c=$h(wh(n,e.center),e.axis),d=Math.min(h,c),p=Math.max(h,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||u2)return Ph(s);if(i.length){const t=i.map(({room:t,edge:i})=>Ch(t,i,s,e.epsilon));if(t.some(t=>!t))return Ph(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Rh(t,e,s)}const Fh="1.72.0-beta.2",Nh={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Hh={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},Lh=2e3,Eh=1e3,Oh=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},Bh=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},Wh=1e3,jh=2e3,Gh=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},qh="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";class Uh{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._resizePreviewNodes=null,this._resizeBaselineLimits=[],this._resizeBaseFrameStable=!0,this._supportPreviewGeneration=0,this._decorAssetGuardReplace=null,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=Oh(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Xt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?Xt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Oh(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&Zt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&Qt(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=te(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=Oh(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const l=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,l||this.host.requestUpdate()},this._deletePhysicalSelection=async()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void await this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=async()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._space;if(!t||!this.host._curSpaceCfg)return;if(!await this.host._confirmDanger({key:"delete-draft",kind:"destructive",title:this.host._t("confirm.delete_draft_title"),message:this.host._t("confirm.delete_draft_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")})||this.host._space!==e)return;const s=this.host._curSpaceCfg;if(!s||!(s.room_drafts||[]).some(e=>e.id===t))return;const i=this._geometrySnapshot();s.room_drafts=(s.room_drafts||[]).filter(e=>e.id!==t),s.room_drafts.length||delete s.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),i)},this._deleteDraftSegment=async()=>{const t=this.host._physicalDialog,e=this.host._space;if(!t||"draft"!==t.kind||!this.host._curSpaceCfg)return;const s=t.id,i=t.segment||0,o=await this.host._confirmDanger({key:"delete-draft-segment",kind:"destructive",title:this.host._t("confirm.delete_draft_segment_title"),message:this.host._t("confirm.delete_draft_segment_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._physicalDialog;if(!o||this.host._space!==e||"draft"!==a?.kind||a.id!==s||(a.segment||0)!==i)return;const n=this.host._curSpaceCfg;if(!n)return;const r=(n.room_drafts||[]).findIndex(t=>t.id===s);if(r<0)return;const l=n.room_drafts[r];if(!Array.isArray(l.segments)||!Number.isInteger(i)||i<0||i>=l.segments.length)return;const h=i,c=[],d=l.points.slice(0,h+1),p=l.points.slice(h+1);if(d.length>=2&&c.push({id:l.id,points:d,segments:l.segments.slice(0,h)}),p.length>=2&&c.push({id:c.length?`${l.id}-${Date.now().toString(36)}`:l.id,points:p,segments:l.segments.slice(h+1)}),2===c.length&&n.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const u=this._geometrySnapshot();n.room_drafts.splice(r,1,...c),n.room_drafts.length||delete n.room_drafts,this.host._activeDraftId===l.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=c.length?{kind:"draft",id:c[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),u)},this._furnShiftListener=t=>{if("Shift"!==t.key)return;const e=this.host._furnPreviewInput;if(!e)return;const s="keydown"===t.type;e.free!==s&&(this.host._furnPreviewInput={...e,free:s},this.host.requestUpdate())},this._furnShiftAttached=!1,this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=Tr(lt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>jh)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot(),h=ee(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*Wh,Number(e.a?.[1])*Wh],i=[Number(e.b?.[0])*Wh,Number(e.b?.[1])*Wh];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/Wh,a.interval.a[1]/Wh],b:[a.interval.b[0]/Wh,a.interval.b[1]/Wh],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*Wh,i.a[1]*Wh],b:[i.b[0]*Wh,i.b[1]*Wh]};e.host={kind:"partition",id:i.id,t:zr([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(h,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>jh)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=or(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,ht);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:re(this.host._settings,{bgColor:null})||null,northDeg:ne(this.host._settings,{}),bgMode:ae(this.host._settings,{}),sunRays:oe(this.host._settings,{}),showRoomTooltip:ie(this.host._settings),zigbeeTopology:se(this.host._settings),busy:!1}},this._openSupportDialog=()=>{this.host._norm&&this.host._canEdit&&(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPreviewGeneration+=1,this.host._supportDialog={draftId:dh("draft"),idempotencyKey:dh("report"),submissionFingerprint:"",contact:"",message:"",attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:"",reportId:""})},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._decorStylePersistTimer=null,this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:le).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},this._discoveryPreviewMemo=null,this._decorImages=new vh(t,{decorSnap:(t,e)=>this._decorSnap(t,e),geometrySnapshot:()=>this._geometrySnapshot(),clearFurniturePreview:()=>this._clearFurniturePreview(),recordGeometry:(t,e)=>this._recordGeometry(t,e),saveConfig:()=>this._saveConfig(),saveShape:()=>this._decorSaveShape(),setGuardReplace:t=>{this._decorAssetGuardReplace=t},furnShiftDetach:()=>this._furnShiftDetach(),furnPick:t=>this._furnPick(t),furnFieldValue:t=>this._furnFieldValue(t),furnFieldToCm:t=>this._furnFieldToCm(t)}),t._editorSecondary=new Br({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Xa}_routeLiveEditorUpdate(t,e){const s=Bo(this.host,t,e);return!s&&this.host._resize.preview&&(this._resizeBaseFrameStable=!1),s}_commitLiveEditor(){!function(t){const e=t,s=Eo(t);s.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(s.raf),s.raf=0,Go(s);const i=e.renderRoot?.querySelector("[data-hp-live-editor]");i&&g(d,i);const o=e.renderRoot?.querySelector("[data-hp-live-editor-html]");if(o){const t=e._viewOr(e._baseVb());g(e._renderLiveEditorMeasurements(t),o)}}(this.host)}_disposeLiveEditor(){!function(t){Ao(t);const e=Fo.get(t);e&&(e.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(e.raf),Go(e),Fo.delete(t))}(this.host)}_queuePointerMove(t,e){zo(this.host,t,e)}_flushPointerMove(t){Ro(this.host,t)}_cancelPointerMove(t){Ao(this.host,t)}_help(t){const e=`${t}.aria`,s=l(this.host.hass,this.host._config?.language);return he(s,t)&&he(s,e)?p``:d}_setMode(t,e=!0){t!==this.host._mode&&this.host._cancelDangerConfirm(),this.host._endTabDrag(),this._clearFurniturePreview(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if("devices"===this.host._mode&&this.host._cancelDeviceDrag(),this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const l=!s.bg&&"view"===t!=("view"===i),h=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=h?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=h?h.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=h?h.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}l&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?ce([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const u=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(u,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&u===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=de(ht,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:de(e,this.host._imperial),max:de(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=il(s,{pitch:W,seedIds:e,geometry:al(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,ol(t.openings,t.partitions,i.openingMoves,{coordScale:Wh,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=ar(this.host._path,or(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,ht)),s=e.length;if((t.partitions||[]).length+s>jh)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=jh:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[pe(ue(t[0],e)),pe(ue(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?_e(s,t,this.host._gridPitch,so):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=gt(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=Yn({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=Jn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=Yn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?sr(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(Bn(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??On,o=s.excludePoints||[],a=tr(Qn(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=st([e[0],e[1]],t);if(r>s.tolerance)continue;const l=er(a,e,s.gridStep);!Xn(l,o,i)&&Zn(r,a.key,n)&&(n={kind:"line",point:l,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,l=r?[...r.point]:this._snapDrawPoint(t,e),h=i?me(i,l):l;return{point:h,candidate:r&&ft(h,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return ft(t,e)}_dropLegacySegments(t=this.host._serverCfg){for(const e of t?.markers||[])"ripple"===e.display&&(e.display="icon_ripple");for(const e of t?.spaces||[]){delete e.segments;const t=new Set,s=e=>"string"==typeof e&&e.length>=1&&e.length<=64&&!t.has(e),i=e=>(t.add(e),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Ut);if(Array.isArray(e.partitions)&&(e.partitions=e.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),e.partitions.length||delete e.partitions),Array.isArray(e.wall_columns)&&(e.wall_columns=e.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:te(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:ge(t.angle)}})),e.wall_columns.length||delete e.wall_columns),Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=Al(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),e.room_drafts.length||delete e.room_drafts),Array.isArray(e.walls)){const t=fe(Et({spaces:[e]}),e.id),s=t?be(e,t,Wh,.02*Tt).contour.map(t=>t.map(t=>t/Wh)):yt(e.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);e.walls=zt(e.walls,e.rooms||[],W,1,s),e.walls.length||delete e.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=_(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_prepareConfigCandidate(t){return this._dropLegacySegments(t),ve(t)}_writeConfig(t=null){this.host._writesPending++,this.host._writeChain=ye(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;const e=_(this.host._serverCfg),s=this._prepareConfigCandidate(this.host._serverCfg),i=_(s);t&&e===t.attemptedFingerprint&&Object.assign(t,{revision:this.host._cfgRev,attempted:s,attemptedFingerprint:i});const o=[...this.host._pendingPhysicalWrites.entries()];for(const[t,e]of o){const i=s.spaces.find(e=>e.id===t),o=$e(i);if(o===e.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(s,t).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(e.before),this.host._pendingPhysicalWrites.delete(t),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});e.fingerprint=o}i!==_(this.host._serverCfg)&&(this.host._serverCfg=s),this.host._cfgContentFingerprint=i;try{await this.host._sendConfigCandidate(s)}catch(t){if(!this._rollbackRejectedPhysicalWrites(o))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of o)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),l=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=l.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof Ml?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);return o?function(t,e,s,i,o){const a=(t?.spaces||[]).find(t=>t?.id===e);if(!a)return[];const n=Number(a.cell_cm)>0?Number(a.cell_cm):5,r=[...Sn(s),...In(s,n,W),...zn(s,n,W)];let l=i?.multiWallNodes||null;if(!l)try{l=J(a.rooms||[],a.walls||[],[],W,n,W,1)}catch{l=null}const h=i&&"lightweight"!==i.status?i:null;let c="ok"===h?.status||"degraded-extra"===h?.status?h.roomGeom:null;const d=null===i||"lightweight"===i?.status;if(!c&&!d&&l?.nodes.length)try{const t=Y(a.rooms||[],a.walls||[],[],[],W,n,W,1);c="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{c=null}for(const t of a.rooms||[]){const e=String(t?.id||"");if(!e||o&&!o.has(e))continue;let s=null;try{s=X(a.rooms||[],e,a.walls||[],[],W,n,W,1,d?null:c??void 0,l)}catch{s=null}r.push(...Rn(e,s,n,W))}return r}(t,e,this._limitSegmentsOf(o),s,i):[]}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i,o){const a=o?.length?new Set(o):void 0;let n;const r=(e.spaces||[]).find(t=>t?.id===s);let l="";try{l=$e(r)}catch{l=""}const h=!a&&e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(h&&l&&h.fingerprint===l&&h.spaceId===s&&(n=h.violations),!n){try{const t=Number(e?.model_version||0)>=9?e:Xl(e).config;n=this.host._junctionLimitViolations(t,s,a?null:void 0,a)}catch{return[]}!a&&e&&"object"==typeof e&&l&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:l,violations:n})}let c=[],d=n;try{c=this.host._junctionLimitViolations(t,s,i,a),a&&(d=n.filter(t=>"clearance"!==t.rule||a.has(t.subject)))}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return An(c,d)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||$e(e)===$e(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let l=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new Ml("invalid-room",e.spaceId);const s=Xl(t).config;l=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new Ml("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),h=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of h?.wall_segments||[])t.cm=St(o.walls,t.a,t.b,W,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=Ul(s,Gl(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=at(e),l=(s.rooms||[]).find(e=>e.id===t.id),h=at(l),c=at(n);if(r&&h&&c&&r.length===h.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;aSl||Hl(r,s,i)>Sl)continue;const l=Nl(n,s,i),h=Nl(r,s,i);l<-Sl||h>1+Sl||h<=l+Sl||o.push({start:l,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,h[e],h[(e+1)%h.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/Wh,t[1]/Wh]):[];i.push(...s.map(t=>[t[0],t[1]])),h=Fl(t)<=Fl(l,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{h=!1}if(!h)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Ql(i,n),this._recordGeometry(t,l);const p=i.spaces.find(t=>t.id===e.spaceId),u=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:u?.before||l,fingerprint:$e(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this._clearFurniturePreview(),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){return clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._furnTouchPending?.pid===t.pointerId?(this._clearFurniturePreview(),this.host._pointers.delete(t.pointerId),void this.host.requestUpdate()):this.host._physicalDrag?.pid!==t.pointerId&&this.host._physicalRotate?.pid!==t.pointerId?this.host._decorDraft?.pid===t.pointerId?(this.host._decorDraft=null,void this.host.requestUpdate()):void(this.host._decorMove?.pid!==t.pointerId&&this.host._dtDrag?.pid!==t.pointerId&&this.host._bdDrag?.pid!==t.pointerId?("opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover(),this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),this.host._viewportGestureDirty&&0===this.host._pointers.size&&(this.host._viewportGestureDirty=!1,this.host.requestUpdate())):this._cancelDecorGesture()):void this._cancelPhysicalGesture()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=$e(s)!==$e(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Xl(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Ql(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:$e(n)})}else Ql(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=at(e);return!!s&&we(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=at(e);return!!s&&E(t,s)})}_pointInRoom(t,e){return e.poly?ke(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!xa(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>jh)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=me(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this.host._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const h=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(h)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),l=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),h=this.host._path[this.host._path.length-1],c=this._samePt(h,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),u=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],_=[...p,...null==d?[]:[{cm:d}],...l],m=u.length>=4&&this._samePt(u[0],u[u.length-1]),g=u,f=_;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||B(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/Wh,t[1]/Wh]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=or(i,e,this.host._drawWallCm,ht);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*Wh,Number(t?.[1])*Wh]):[],o=Array.isArray(e?.segments)?e.segments:[];return or(Math.max(0,t.length-1),s,this.host._drawWallCm,ht).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/Wh,t[1]/Wh]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=Dr(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=Er(r,{requiredSourceKey:o,maxDistance:x(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=at(t);if(!t.id||!e)continue;const s=xe(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=B(i)>=B(o)?i:o,r=a===i?o:i,l=[...n].sort((t,e)=>Math.abs(t.area-B(r))-Math.abs(e.area-B(r))||t.key.localeCompare(e.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const l=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=Cr(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=Er(this._wallGraphSources([]),{point:t,maxDistance:x(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=Er(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(Xt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:te(s),angle:0},a=(i.wall_columns||[]).find(t=>Zt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/Wh,e[1]/Wh]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:de(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:de(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?ge(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:de(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),Gh(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Se(o,Me(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=De(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Se(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),l=this.host._physicalPickCycle,h=a.length>1&&l?.signature===n&&r-l.at>380&&r-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:h,x:t.clientX,y:t.clientY,at:r},e=a[h].kind,s=a[h].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Ut*Wh-Math.min(...o),Math.min(Ut*Wh-Math.max(...o),s[0])),Math.max(-Ut*Wh-Math.min(...a),Math.min(Ut*Wh-Math.max(...a),s[1]))]}_physicalMove(t){t.stopPropagation(),zo(this.host,"physical",()=>this._physicalMoveNow(t))}_physicalMoveNow(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){Ro(this.host,"physical");const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/Wh,o.a[1]/Wh],t.b=[o.b[0]/Wh,o.b[1]/Wh];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=nt(t,[o],Wh,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,rt(t,s,Wh))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&Zt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/Wh,(o.center[1]+e.delta[1])/Wh])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){Ao(this.host,"physical"),Ao(this.host,"physical-rotate"),this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),Gh(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:ge(e.angle),angle:ge(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){t.preventDefault(),t.stopPropagation(),zo(this.host,"physical-rotate",()=>this._physicalRotateMoveNow(t))}_physicalRotateMoveNow(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=ge(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){Ro(this.host,"physical-rotate");const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&Zt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?at(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:x(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*Wh,t.a[1]*this.host._spaceH],b:[t.b[0]*Wh,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?x(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!xa(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],l=n.poly[(i+1)%n.poly.length],h=Ca(r,l,a);if(!h)return{enabled:!1,reason:"diagonal"};if(!za(n.poly,i,h,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(Fa(t,r,l,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=ya(fa(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!xa(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!za(t.room.poly,t.edge,h,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(wa([t.x,t.y],r,l)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=La(t,e,o,1,a),l=La(t,e,n,0,a);if(!r||!l)return{enabled:!1,reason:"partial-shared"};v.push(r,l)}const y={roomId:s,edge:i,a:[...r],b:[...l],n:ka(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!qa(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>qa(t,e,y,s,o))){if(!s.some(s=>Ba(Wa(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>qa(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>qa(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const l=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/Wh,t[1]/l]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/Wh,s.y=e[1]/l)}const h=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*Wh,t[1]*l]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/Wh,i[t][1]/Wh],o.b=[i[(t+1)%i.length][0]/Wh,i[(t+1)%i.length][1]/Wh])}}if(h.length&&Array.isArray(r.walls)&&r.walls.length){const t=Ce(r.walls,h,c,this.host._wallKeyPitch,Wh,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=at(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),u=new Map;for(const t of n.walls||[]){const e=p(t);u.set(e,(u.get(e)||0)+1)}const _=[];for(const t of r.walls||[]){const e=p(t),s=u.get(e)||0;s?u.set(e,s-1):_.push(t)}if(Pe(_,d,this.host._wallKeyPitch,Wh,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=function(t,e,s=1e-8){const i=Qa(t),o=new Set(e),a=i.flatMap(t=>{if(!o.has(String(t.id||"")))return[];const e=on(sn(t));return e?[e]:[]});return i.flatMap(t=>{const e=String(t.id||""),i=on(sn(t)),n=!!i&&a.some(t=>i.minX<=t.maxX+s&&i.maxX+s>=t.minX&&i.minY<=t.maxY+s&&i.maxY+s>=t.minY);return e&&(o.has(e)||n)?[e]:[]})}(r.rooms||[],i),g=an(r,m);if(!g||!this._rszSpaceCandidateGeometry(this.host._space,g).ok)return{ok:!1,reason:"physical-geometry"};const f={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)};try{this._resizePreviewNodes=J(g.rooms,g.walls,[],W,this.host._cellCm,this.host._gridPitch,Wh)}catch{this._resizePreviewNodes=null}let b;try{const t=new Set(i),e=this.host._junctionLimitViolations(f,this.host._space,{status:"lightweight",multiWallNodes:this._resizePreviewNodes},t);b=An(e,this._resizeBaselineLimits.filter(e=>"clearance"!==e.rule||t.has(e.subject)))}catch{b=[{rule:"check_failed",subject:this.host._space,actual:0,limit:0}]}return b.length?(this.host._rszLimitViolation=b[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:null}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,this.host._physicalBodiesCache&&(this.host._physicalBodiesCache.key=`${this.host._space}|${this.host._cfgEpoch}|${this.host._cellCm}|${this.host._gridPitch}`),!t||!e)return;const s=Ie(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:_([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};Bh(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;this._resizeBaseFrameStable=!0,t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));Gh(t);const a=o.plan;try{this._resizeBaselineLimits=this.host._junctionLimitViolations(this.host._serverCfg,this.host._space,null,new Set(a.roomIds))}catch{this._resizeBaselineLimits=[]}const n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,l=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,h=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:h,before:JSON.parse(h),wallUnionBefore:l,epochBefore:this.host._cfgEpoch})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),zo(this.host,"resize",()=>this._rszMoveNow(t)))}_rszMoveNow(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(Ro(this.host,"resize"),!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){Ao(this.host,"resize");const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(null!==e.restoreEpoch?this.host._cfgEpoch=e.restoreEpoch:this.host._cfgEpoch++,this.host._physicalBodiesCache&&(this.host._physicalBodiesCache.key=`${this.host._space}|${this.host._cfgEpoch}|${this.host._cellCm}|${this.host._gridPitch}`),e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};Bh(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}null!==e.restoreEpoch&&"plan"===this.host._mode&&this._resizeBaseFrameStable&&(this.host._terminalFrame=1),this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):Te(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const l="mi"===this.host.hass?.config?.unit_system?.length,h=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),u=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this.host._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?ze(b>8?2.5:b,f,u.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of h){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&X(f.rooms,s,c,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh,null,this._resizePreviewNodes)||a,r=d.length?Re(Ae(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:Fe(n,this.host._gridPitch,this.host._cellCm),h=Ne(r,l),p=rn({poly:a,edge:e.edgeByRoom[s],text:h,view:g,gearCenter:He(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:h,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=Le(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>Ee(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return d;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return f``}_renderResizeLayer(t,e){const s=Math.max(.013*t.w,5),i=s/2,o=t=>t.toFixed(1),a=`M ${o(-.7*i)} 0 H ${o(.7*i)} M 0 ${o(-.22*i)} V ${o(-i)} M ${o(-.32*i)} ${o(-.6*i)} L 0 ${o(-i)} L ${o(.32*i)} ${o(-.6*i)} M 0 ${o(.22*i)} V ${o(i)} M ${o(-.32*i)} ${o(.6*i)} L 0 ${o(i)} L ${o(.32*i)} ${o(.6*i)}`,n=[],r=this._rszRooms(),l=this.host._resize.snapshotIdentity||this._rszSnapshot(),h=e?.length?new Set(e):null;for(const t of r)if(!h||h.has(t.id))for(let e=0;ethis._rszEdgeDown(s,t.id,e)} + @pointermove=${t=>this._rszMove(t)} + @pointerup=${t=>this._rszUp(t)} + @pointercancel=${t=>this._rszPointerCancel(t)} + @lostpointercapture=${t=>this._rszPointerCancel(t)} + @click=${u?t=>this._rszDisabledActivate(t,p.reason):null} + @keydown=${u?t=>this._rszDisabledKey(t,p.reason):null}> + ${_} + `),n.push(f``)}return f`${n}`}_partitionOpeningCuts(t=this.host._spaceModel(),e=()=>!0){if(!t)return[];const s=this.host._curSpaceCfg?.id===t.id?this.host._curSpaceCfg:null,i=Oe(s,t,this.host._cellCm,this.host._gridPitch,Wh);return Be(i,e)}_decorSnapGeometry(t){const e=this.host._spaceModel();if(!e)return{points:[],segments:[]};const s=t||"",i=this.host._decorSnapCache;if(i&&i.epoch===this.host._cfgEpoch&&i.space===this.host._space&&i.height===this.host._decorH&&i.exclude===s)return i.geometry;const o=[];for(const e of this.host._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*Wh,e.y1*this.host._decorH],s=[e.x2*Wh,e.y2*this.host._decorH];o.push({points:[t,s,[(t[0]+s[0])/2,(t[1]+s[1])/2]],segments:[{a:t,b:s}]})}else if("text"===e.kind)o.push({points:[[e.x*Wh,e.y*this.host._decorH]],segments:[]});else{const t=this.host._decorBoxOf(e);t&&o.push(We(t))}for(const t of e.rooms){const e=at(t);e?.length&&o.push({points:e.flatMap((t,s)=>{const i=e[(s+1)%e.length];return[t,[(t[0]+i[0])/2,(t[1]+i[1])/2]]}),segments:e.map((t,s)=>({a:t,b:e[(s+1)%e.length]}))})}const a=je(o);return this.host._decorSnapCache={epoch:this.host._cfgEpoch,space:this.host._space,height:this.host._decorH,exclude:s,geometry:a},a}_decorSnap(t,e="mouse",s){const i=this.host._stageEl,o=this.host._viewOr(this.host._baseVb()),a="touch"===e||"pen"===e?14:8,n=i?o.w/Math.max(1,i.clientWidth)*a:this.host._gridPitch;return Ge(t,this._decorSnapGeometry(s),n,t=>this._snap(t)).point}_replaceDecor(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>s.id===t?{...s,...e}:s),this.host.requestUpdate())}_cancelDecorGesture(){Ao(this.host,"decor-transform"),Ao(this.host,"decor-move"),Ao(this.host,"backdrop");const t=this.host._decorMove?.before||this.host._dtDrag?.before||this.host._bdDrag?.before,e=t&&this.host._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const s=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=s(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,s(t.plan_transform||{})),this.host._decorSnapCache=null}this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null,t&&e&&"decor"===this.host._mode&&(this.host._terminalFrame=1),this.host.requestUpdate()}_decorPointerDown(t){const e=this.host._decorTool;if("mouse"===t.pointerType&&0!==t.button&&"select"!==e&&"erase"!==e)return!1;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const s=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorDraft={kind:e,a:s,b:s,pid:t.pointerId},Gh(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorTextDialog={x:G(e[0]/Wh),y:G(e[1]/this.host._decorH),text:"",color:this.host._decorStyle.color,opacity:this.host._decorStyle.opacity,angle:"0",sizeCm:qe(Ft,this.host._cellCm,this.host._gridPitch)},this.host._decorTextSelection={start:0,end:0},!0}if("furniture"===e||"image"===e){if("furniture"===e?!this.host._furnPalette:!this.host._decorImagePalette)return!1;t.preventDefault();const s=t.pointerType||"mouse";if("mouse"===s)return"furniture"===e?this._furnPlace(this._svgPoint(t),t.shiftKey,s):this._decorImagePlace(this._svgPoint(t),s),!0;const i=this.host._furnTouchPending;return i&&i.pid!==t.pointerId?(i.cancelled=!0,this.host._furnPreviewInput=null,!0):(this.host._furnTouchPending={pid:t.pointerId,sx:t.clientX,sy:t.clientY,pointerType:s,cancelled:!1},this.host._furnPreviewInput=null,Gh(t),!0)}if("select"===e&&(this.host._decorSel=null),this.host._bdMovable){const e=this.host._bdRect,s=this._svgPoint(t),i=e.x+e.w/2,o=e.y+e.h/2,a=-Ue(e.angle)*Math.PI/180,n=s[0]-i,r=s[1]-o,l=[i+n*Math.cos(a)-r*Math.sin(a),o+n*Math.sin(a)+r*Math.cos(a)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this.host._decorDraft;if(this.host._decorDraft=null,!t)return;const e=.5*this.host._gridPitch;if(!Ke(t.kind,t.a,t.b,e))return;const s=Wh,i=this.host._decorH,o=this.host._decorStyle,a=this._geometrySnapshot(),n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r=G;let l;if("line"===t.kind)l={id:n,kind:"line",x1:r(t.a[0]/s),y1:r(t.a[1]/i),x2:r(t.b[0]/s),y2:r(t.b[1]/i),...Ve(o,!1)};else{const e=r(Math.min(t.a[0],t.b[0])/s),a=r(Math.min(t.a[1],t.b[1])/i),h=Math.abs(t.b[0]-t.a[0])/s,c=Math.abs(t.b[1]-t.a[1])/i;l={id:n,kind:t.kind,x:e,y:a,w:h,h:c,...Ve(o,!0)}}this.host._curSpaceCfg.decor=[...this.host._decorList,l],this.host._decorSel=n,this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this.host._mode)return;const s=this.host._decorTool;if("text"===s){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==s&&"erase"!==s||(t.stopPropagation(),t.preventDefault(),"erase"!==s?(this.host._decorSel=e.id,this.host._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},Gh(t)):this.host._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this.host._decorMove;if(!e)return;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const s=this._svgPoint(t),i=e.orig,o=("line"===i.kind?i.x1:i.x)*Wh,a=("line"===i.kind?i.y1:i.y)*this.host._decorH,n=this._decorSnap([o+(s[0]-e.start[0]),a+(s[1]-e.start[1])],t.pointerType,e.id);let r=(n[0]-o)/Wh,l=(n[1]-a)/this.host._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=Ut;r=Math.max(-_-c,Math.min(_-c-p,r)),l=Math.max(-_-d,Math.min(_-d-u,l)),(r||l)&&(e.moved=!0);this.host._curSpaceCfg.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s=e.orig;return"line"===t.kind?{...t,x1:s.x1+r,y1:s.y1+l,x2:s.x2+r,y2:s.y2+l}:{...t,x:s.x+r,y:s.y+l}}),this.host.requestUpdate()}_dmUp(){Ro(this.host,"decor-move");const t=this.host._decorMove;t?.moved&&(this._recordGeometry(this.host._t("history.decor_move"),t.before),this._saveConfig()),this.host._decorMove=null,this.host.requestUpdate()}_decorShapeDbl(t,e){"decor"===this.host._mode&&"select"===this.host._decorTool&&(t.preventDefault(),t.stopPropagation(),this.host._decorMove=null,this.host._decorSel=e.id,this._openDecorProperties(e))}_openDecorProperties(t){if("decor"!==this.host._mode||"select"!==this.host._decorTool)return;if("text"===t.kind)return void this._decorOpenText(t);if(!["line","rect","ellipse","furniture","image"].includes(t.kind))return;"image"===t.kind&&this._decorImageCatalogLoad();const e=this.host._decorResolvedStyle(t),s="line"===t.kind?t:null,i=this.host._decorBoxOf(t);this.host._decorShapeDialog={id:t.id,kind:t.kind,color:e.color,opacity:e.opacity,widthCm:e.widthCm,angle:this.host._angleField(Ue(s?Je([s.x1*Wh,s.y1*this.host._decorH],[s.x2*Wh,s.y2*this.host._decorH]):t.angle)),...s?{lengthCm:qe(Math.hypot((s.x2-s.x1)*Wh,(s.y2-s.y1)*this.host._decorH),this.host._cellCm,this.host._gridPitch),lineStyle:"dashed"===s.line_style?"dashed":"solid"}:{},...i?{sizeWCm:qe(i.w,this.host._cellCm,this.host._gridPitch),sizeHCm:qe(i.h,this.host._cellCm,this.host._gridPitch)}:{},..."furniture"===t.kind?{symbol:t.symbol,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Kt(qe(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Kt(qe(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."image"===t.kind?{assetId:t.asset_id,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Kt(qe(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Kt(qe(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."rect"===t.kind||"ellipse"===t.kind?{fill:e.fill,fillColor:e.fillColor,fillOpacity:e.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const s=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Ht(t[1])),i=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||i?"":Lt(t.entity,o),n=!(!String(t.entity??"").trim()||s||a&&!i);if(a&&!n){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+a+e.slice(t+2):`${e}${e?" ":""}${a}`}this.host._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:D(t.color,this.host._decorStyle.color),opacity:Ye(t.opacity,this.host._decorStyle.opacity),angle:this.host._angleField(t.angle),sizeCm:this.host._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:n||void 0},this.host._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this.host._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this.host._decorTextDialog;if(!e)return;const s=Lt(e.pickerEntity,t);if(!s)return;const i=e.text,o=Math.max(0,Math.min(i.length,this.host._decorTextSelection.start)),a=Math.max(o,Math.min(i.length,this.host._decorTextSelection.end));if(i.length-(a-o)+s.length>200)return;const n=i.slice(0,o)+s+i.slice(a),r=o+s.length;this.host._decorTextSelection={start:r,end:r},this.host._decorTextDialog={...e,text:n,preserveLegacy:void 0},this.host.updateComplete.then(()=>{const t=this.host.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(r,r))})}_decorSaveText(){const t=this.host._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this.host._decorTextDialog=null);const s=this._geometrySnapshot(),i=this.host._curSpaceCfg,o={color:t.color,opacity:Ye(t.opacity),size_cm:Number(Math.max(.1,Math.min(Lh,t.sizeCm)).toFixed(4)),...Ue(t.angle)?{angle:Ue(t.angle)}:{}};if(t.id)i.decor=this.host._decorList.map(s=>{if(s.id!==t.id)return s;if("text"!==s.kind)return s;if(t.preserveLegacy){const{angle:t,size:i,scale:a,...n}=s;return{...n,text:e,...o}}const{entity:i,attr:a,unit:n,...r}=s,{angle:l,size:h,scale:c,...d}=r;return{...d,text:e,...o}});else{const s="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this.host._decorList,{id:s,kind:"text",x:t.x,y:t.y,text:e,...o}],this.host._decorSel=s}this.host._decorTextDialog=null,this._recordGeometry(this.host._t(t.id?"history.decor_edit":"history.decor_add"),s),this._saveConfig(),this.host.requestUpdate()}_decorSaveShape(){const t=this.host._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),s={color:t.color,opacity:Ye(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Ye(t.fillOpacity,.25)},i=this.host._curSpaceCfg,o="furniture"===t.kind||"image"===t.kind?qt(t.sizeWField,this.host._imperial,Ut*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?qt(t.sizeHField,this.host._imperial,Ut*this.host._cellCm):null;("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a)&&(i.decor=this.host._decorList.map(e=>{if(e.id!==t.id)return e;const i="rect"===t.kind||"ellipse"===t.kind,n=Ve(s,i);if("line"===e.kind){const s=(e.x1+e.x2)/2*Wh,i=(e.y1+e.y2)/2*this.host._decorH,o=Bt(Math.max(.1,Number(t.lengthCm)||.1),this.host._cellCm,this.host._gridPitch),a=Ue(t.angle)*Math.PI/180,r=Math.cos(a)*o/2,l=Math.sin(a)*o/2,h=this._snap([s-r,i-l]),c=this._snap([s+r,i+l]),{width:d,line_style:p,...u}=e;return{...u,...n,x1:G(h[0]/Wh),y1:G(h[1]/this.host._decorH),x2:G(c[0]/Wh),y2:G(c[1]/this.host._decorH),..."dashed"===t.lineStyle?{line_style:"dashed"}:{}}}if("furniture"===e.kind){const s=e.w*Wh,i=e.h*this.host._decorH,r=Bt(Math.abs(o),this.host._cellCm,this.host._gridPitch),l=Bt(Math.abs(a),this.host._cellCm,this.host._gridPitch),h=e.x*Wh+s/2,c=e.y*this.host._decorH+i/2,d=Ue(t.angle),{width:p,angle:u,flip_h:_,flip_v:m,...g}=e;return{...g,...n,x:G((h-r/2)/Wh),y:G((c-l/2)/this.host._decorH),w:r/Wh,h:l/this.host._decorH,...t.symbol?{symbol:t.symbol}:{},...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...d?{angle:d}:{}}}if("image"===e.kind){const s=e.w*Wh,i=e.h*this.host._decorH,n=Bt(Math.abs(o),this.host._cellCm,this.host._gridPitch),r=Bt(Math.abs(a),this.host._cellCm,this.host._gridPitch),l=e.x*Wh+s/2,h=e.y*this.host._decorH+i/2,c=Ue(t.angle);return{id:e.id,kind:"image",asset_id:t.assetId||e.asset_id,x:G((l-n/2)/Wh),y:G((h-r/2)/this.host._decorH),w:n/Wh,h:r/this.host._decorH,opacity:Ye(t.opacity,1),...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...c?{angle:c}:{}}}if("rect"===e.kind||"ellipse"===e.kind){const s=e.w*Wh,i=e.h*this.host._decorH,o=Math.max(this.host._gridPitch,ue(Bt(Number(t.sizeWCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),a=Math.max(this.host._gridPitch,ue(Bt(Number(t.sizeHCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),r=e.x*Wh+s/2,l=e.y*this.host._decorH+i/2,h=Ue(t.angle),c=Xe({x:r-o/2,y:l-a/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...n,x:G(c[0]/Wh),y:G(c[1]/this.host._decorH),w:o/Wh,h:a/this.host._decorH,...h?{angle:h}:{}}}return e}),"image"!==t.kind&&this._updateDecorStyle({...s,fill:"rect"===t.kind||"ellipse"===t.kind?s.fill:this.host._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?s.fillColor:this.host._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?s.fillOpacity:this.host._decorStyle.fillOpacity}),this.host._decorShapeDialog=null,this._recordGeometry(this.host._t("history.decor_edit"),e),this._saveConfig(),this.host.requestUpdate())}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*Wh,(t.y1+t.y2)/2*this.host._decorH]:"furniture"===t.kind||"image"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*Wh,(t.y+t.h/2)*this.host._decorH]:[t.x*Wh,t.y*this.host._decorH]}_dtApply(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const i={...s};"text"===s.kind&&void 0!==e.textSizeCm&&(delete i.size,delete i.scale);const o={...i};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(Lh,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this.host._cfgEpoch++,this.host.requestUpdate())}_dtStart(t,e,s,i){const o=this.host._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[a,n]=this._dtPivot(o),r=this._svgPoint(t),l=this.host._decorBoxOf(o);this.host._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:a,ay:n,r0:Math.hypot(r[0]-a,r[1]-n),a0:180*Math.atan2(r[1]-n,r[0]-a)/Math.PI,textSizeCm0:"text"===o.kind?this.host._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:s?.[0],sgy:s?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:i,moved:!1},Gh(t)}_dtMove(t){const e=this.host._dtDrag;if(!e)return;const s=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const i=this._decorSnap(s,t.pointerType,e.id),o=G(i[0]/Wh),a=G(i[1]/this.host._decorH),n=e.origShape,r=0===e.lineEnd?n.x1:n.x2,l=0===e.lineEnd?n.y1:n.y2;return(Math.abs(o-r)>1e-9||Math.abs(a-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:a}:{x2:o,y2:a})}if("scale"===e.kind&&e.orig){if("furniture"===e.origShape.kind||"image"===e.origShape.kind){const i=Ze({...e.orig,flip_h:e.origShape.flip_h,flip_v:e.origShape.flip_v},e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,Bt(.1,this.host._cellCm,this.host._gridPitch)),o=Math.abs(i.x-e.orig.x)>1e-9||Math.abs(i.y-e.orig.y)>1e-9||Math.abs(i.w-e.orig.w)>1e-9||Math.abs(i.h-e.orig.h)>1e-9||!!i.flip_h!=!!e.origShape.flip_h||!!i.flip_v!=!!e.origShape.flip_v;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyFurnitureBox(e.id,i)}const i=Qe(e.orig,e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,this.host._gridPitch,this.host._gridPitch),o=Math.abs(i.x-e.orig.x)>1e-6||Math.abs(i.y-e.orig.y)>1e-6||Math.abs(i.w-e.orig.w)>1e-6||Math.abs(i.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,i)}if("scale"===e.kind){const t=Math.hypot(s[0]-e.ax,s[1]-e.ay);if(e.r0<1e-6)return;const i=Math.max(.1,Math.min(Lh,e.textSizeCm0*(t/e.r0))),o=Math.abs(i-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:i})}const i=180*Math.atan2(s[1]-e.ay,s[0]-e.ax)/Math.PI;let o;"furniture"===e.origShape.kind||"image"===e.origShape.kind?o=ts(e.angle0,e.a0,i,t.shiftKey):(o=e.angle0+(i-e.a0),t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360));const a=Math.abs(o-e.angle0)>1e-6;(a||e.moved)&&(e.moved||=a,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this.host._dtDrag;this.host._dtDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.decor_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_dtMeasure(){!function(t){const e=t,s=e._dtSel;if(!s)return void(e._dtBox&&(e._dtBox=null,e.requestUpdate()));if("text"!==s.kind)return;const i=e.renderRoot.querySelector(`text.dtext[data-id="${s.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let o;try{o=i.getBBox()}catch{return}if(!o||!o.width&&!o.height)return;const a={id:s.id,x:o.x,y:o.y,w:o.width,h:o.height},n=e._dtBox;n?.id===a.id&&Math.abs(n.x-a.x)<.01&&Math.abs(n.y-a.y)<.01&&Math.abs(n.w-a.w)<.01&&Math.abs(n.h-a.h)<.01||(e._dtBox=a,e.requestUpdate())}(this.host)}_deleteDecor(t){if(!this.host._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this.host._curSpaceCfg.decor=this.host._decorList.filter(e=>e.id!==t),this.host._decorSel===t&&(this.host._decorSel=null),this._recordGeometry(this.host._t("history.decor_delete"),e),this._saveConfig(),this.host.requestUpdate()}_decorDeleteSel(){this.host._decorSel&&this._deleteDecor(this.host._decorSel)}_confirmDecorErase(){const t=this.host._decorEraseConfirm;this.host._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}_furnFieldValue(t){return Math.round(100*(this.host._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return es(this.host._imperial?30.48*t:100*t)}_furnShiftAttach(){this._furnShiftAttached||(this._furnShiftAttached=!0,window.addEventListener("keydown",this._furnShiftListener),window.addEventListener("keyup",this._furnShiftListener))}_furnShiftDetach(){this._furnShiftAttached&&(this._furnShiftAttached=!1,window.removeEventListener("keydown",this._furnShiftListener),window.removeEventListener("keyup",this._furnShiftListener))}_furnPick(t){const e=ss(t);this.host._furnPalette={symbol:t,w:e.w,h:e.h},this._furnShiftAttach()}get _furnWalls(){return function(t){const e=t._spaceModel();if(!e)return[];const s=[e.id,t._cfgEpoch,t._cellCm,t._gridPitch,t._wallKeyPitch].join("|"),i=_n.get(t);if(i?.key===s)return i.value;const o=[...pn(e.rooms,t._spaceWalls,t._openCuts(),t._wallKeyPitch,t._cellCm,t._gridPitch,b),...un(t._rawPhysicalBodiesR())];return _n.set(t,{key:s,value:o}),o}(this.host)}_resolveFurniturePlacement(t,e=!1,s="mouse"){const i=this.host._furnPalette,o=this.host._curSpaceCfg;if(!i||!o)return null;const a=this.host._decorH,n=this._decorSnap(t,s);return function(t){const{symbol:e,widthCm:s,depthCm:i,point:o,canvasW:a,canvasH:n,cellCm:r,gridPitch:l,walls:h,wallReach:c,free:d=!1,intentPoint:p=o,preferredNormal:u}=t;if(!(j(e)&&n>0&&Number.isFinite(o[0])&&Number.isFinite(o[1])))return null;const _=Number.isFinite(p[0])&&Number.isFinite(p[1])?p:o,m=q(U(s,r,l,a)),g=q(U(i,r,l,a));let f=o[0],b=o[1],v=0;const y=d?null:hn(f,b,g*n,h,c,l,_,u);return y&&(f=y.cx,b=y.cy,v=y.angle),{symbol:e,x:G(f/a-m/2),y:G(b/n-g/2),w:m,h:g,angle:Number(v.toFixed(2))}}({symbol:i.symbol,widthCm:i.w,depthCm:i.h,point:[n[0],n[1]],canvasW:1e3,canvasH:a,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,walls:this._furnWalls,wallReach:6*this.host._gridPitch,intentPoint:[t[0],t[1]],free:e})}_furniturePreviewPlacement(){const t=this.host._furnPreviewInput;return t&&"decor"===this.host._mode&&"furniture"===this.host._decorTool&&this.host._furnPalette&&this.host._pointerModality.hoverEnabled?this._resolveFurniturePlacement(t.raw,t.free,"mouse"):null}_renderFurniturePlacementPreview(t){const e=this._furniturePreviewPlacement();if(!e)return d;const s=j(e.symbol);if(!s)return d;const i=is(e,Wh,this.host._decorH,s.viewW,s.viewH),o=this.host._decorStyle,a=os(Bt(o.widthCm,this.host._cellCm,this.host._gridPitch),t);return f``}_furnLive(){const t=this.host._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this.host._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const s=e.w*Wh,i=e.h*this.host._decorH,o=as(e.x*Wh,e.y*this.host._decorH,s,i,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],n=a(o[0],o[1]),r=a(o[0],o[3]);return[{x:n[0],y:n[1],text:this.host._fmtLen([0,0],[s,0])},{x:r[0],y:r[1],text:this.host._fmtLen([0,0],[0,i])}]}_furnPlace(t,e=!1,s="mouse"){const i=this._resolveFurniturePlacement(t,e,s);if(!i)return;const o=this.host._curSpaceCfg;if(!o)return;const a=this._geometrySnapshot(),n="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r={id:n,kind:"furniture",symbol:i.symbol,x:i.x,y:i.y,w:i.w,h:i.h,...Ve(this.host._decorStyle,!1)};i.angle&&(r.angle=i.angle),o.decor=[...this.host._decorList,r],this.host._decorSel=n,this.host._decorTool="select",this.host._furnPalette=null,this.host._furnCategory=null,this._furnShiftDetach(),this._clearFurniturePreview(),this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_clearFurniturePreview(){this.host._furnPreviewInput=null,this.host._furnTouchPending=null}_furnPointerMove(t,e){const s=this.host._furnTouchPending;if(s?.pid===t.pointerId)return Math.abs(t.clientX-s.sx)+Math.abs(t.clientY-s.sy)>8&&(s.cancelled=!0),this.host._furnPreviewInput=null,!0;const i="furniture"===this.host._decorTool?!!this.host._furnPalette:"image"===this.host._decorTool&&!!this.host._decorImagePalette;if("decor"!==this.host._mode||!i)return this._clearFurniturePreview(),!1;if("mouse"!==t.pointerType||!e)return this.host._furnPreviewInput=null,!1;const o=this._svgPoint(t);return this.host._furnPreviewInput={raw:[o[0],o[1]],free:t.shiftKey},!0}_furnPointerLeave(t){this.host._furnTouchPending?.pid===t.pointerId&&(this.host._furnTouchPending.cancelled=!0),this.host._furnPreviewInput=null}_furnPointerUp(t){const e=this.host._furnTouchPending;return!(!e||e.pid!==t.pointerId)&&(this.host._furnTouchPending=null,!e.cancelled&&"decor"===this.host._mode&&("furniture"===this.host._decorTool&&this.host._furnPalette||"image"===this.host._decorTool&&this.host._decorImagePalette)&&("furniture"===this.host._decorTool?this._furnPlace(this._svgPoint(t),t.shiftKey,e.pointerType):this._decorImagePlace(this._svgPoint(t),e.pointerType)),!0)}_furnMoveUpdate(t){const e=this.host._decorMove;if(!e)return;if("furniture"!==e.orig.kind)return;const s=e.orig,i=this.host._curSpaceCfg;if(!i)return;const o=Wh,a=this.host._decorH,n=this._svgPoint(t),r=(s.x+s.w/2)*o+(n[0]-e.start[0]),l=(s.y+s.h/2)*a+(n[1]-e.start[1]);let h,c,d=Number(s.angle)||0;const p=d*Math.PI/180,u=[-Math.sin(p),Math.cos(p)],_=t.shiftKey?null:hn(r,l,s.h*a,this._furnWalls,6*this.host._gridPitch,this.host._gridPitch,[r,l],u);if(_)h=_.cx/o-s.w/2,c=_.cy/a-s.h/2,d=_.angle;else{const i=this._decorSnap([r-s.w/2*o,l-s.h/2*a],t.pointerType,e.id);h=i[0]/o,c=i[1]/a}h=G(h),c=G(c),(Math.abs(h-s.x)>1e-9||Math.abs(c-s.y)>1e-9||Math.abs(d-(Number(s.angle)||0))>1e-9)&&(e.moved=!0),i.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s={...t,x:h,y:c};return d?s.angle=Number(d.toFixed(2)):delete s.angle,s}),this.host.requestUpdate()}_decorApplyBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=Wh,o=this.host._decorH;s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const a=Xe(e,s.angle,t=>this._snap(t));return{...s,x:G(a[0]/i),y:G(a[1]/o),w:Math.max(this.host._gridPitch/i,Math.min(2*Ut,e.w/i)),h:Math.max(this.host._gridPitch/o,Math.min(2*Ut,e.h/o))}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorApplyFurnitureBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=Wh,o=this.host._decorH,a=Bt(.1,this.host._cellCm,this.host._gridPitch);s.decor=this.host._decorList.map(s=>{if(s.id!==t||"furniture"!==s.kind&&"image"!==s.kind)return s;const{flip_h:n,flip_v:r,...l}=s;return{...l,x:G(e.x/i),y:G(e.y/o),w:Math.max(a/i,Math.min(2*Ut,e.w/i)),h:Math.max(a/o,Math.min(2*Ut,e.h/o)),...e.flip_h?{flip_h:!0}:{},...e.flip_v?{flip_v:!0}:{}}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorImagePlace(t,e="mouse"){this._decorImages.place(t,e)}_renderDecorImagePlacementPreview(){return this._decorImages.renderPlacementPreview()}_renderMissingDecorImage(t,e,s,i,o,a,n,r,l){return this._decorImages.renderMissing(t,e,s,i,o,a,n,r,l)}async _decorImageUpload(t,e=!1){await this._decorImages.uploadFromInput(t,e)}async _uploadDecorImage(t,e,s){await this._decorImages.upload(t,e,s)}async _decorImageDelete(t){await this._decorImages.delete(t)}async _decorImageCatalogLoad(){await this._decorImages.loadCatalog()}_renderDecorImagePalette(){return this._decorImages.renderImagePalette()}_renderFurnPalette(){return this._decorImages.renderFurniturePalette()}_openBackdropDialog(t){if(!this.host._bdMovable||!this.host._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this.host._bdDrag=null;const e=this.host._bdRect;this.host._backdropDialog={widthCm:qe(e.w,this.host._cellCm,this.host._gridPitch),heightCm:qe(e.h,this.host._cellCm,this.host._gridPitch),angle:this.host._angleField(e.angle)}}_saveBackdropDialog(){const t=this.host._backdropDialog,e=this.host._bdBase,s=this.host._bdRect;if(!t||!e||!s)return;const i=Ue(t.angle),o=this._geometrySnapshot(),a=Math.min(e.w*At,Math.max(e.w*Rt,ue(Bt(t.widthCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),n=Math.min(e.h*At,Math.max(e.h*Rt,ue(Bt(t.heightCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),r=s.x+s.w/2,l=s.y+s.h/2,h=Xe({x:r-a/2,y:l-n/2},i,t=>this._snap(t));this._bdApply((h[0]-e.x)/Wh,(h[1]-e.y)/Wh,a/e.w,n/e.h,i),this.host._backdropDialog=null,this._recordGeometry(this.host._t("history.backdrop_transform"),o),this._saveConfig()}_bdApply(t,e,s,i,o){const a=this.host._curSpaceCfg;if(!a)return;a.plan_x=Number(G(t).toFixed(6)),a.plan_y=Number(G(e).toFixed(6)),delete a.plan_scale,a.plan_scale_x=Number(Math.min(At,Math.max(Rt,s)).toFixed(6)),a.plan_scale_y=Number(Math.min(At,Math.max(Rt,i)).toFixed(6));const n=Ue(o);n?a.plan_angle=Number(n.toFixed(2)):delete a.plan_angle,this.host._cfgEpoch++,this.host.requestUpdate()}_bdStart(t,e,s=!1){const i=this.host._bdBase,o=this.host._bdRect;if(!i||!o)return!1;const a=this._svgPoint(t),n=e?e[0]:0,r=e?e[1]:0,l=n>0?o.x:o.x+o.w,h=r>0?o.y:o.y+o.h;return this.host._bdDrag={kind:s?"rotate":e?"scale":"move",pid:t.pointerId,sx:a[0],sy:a[1],base:i,p0:this.host._bdParams,fx:l,fy:h,sgx:n,sgy:r,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},Gh(t),!0}_bdMove(t){zo(this.host,"backdrop",()=>this._bdMoveNow(t))}_bdMoveNow(t){const e=this.host._bdDrag;if(!e)return;const s=this._svgPoint(t),i=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,a=this._snap([t+(s[0]-e.sx),o+(s[1]-e.sy)]),n=Math.abs(a[0]-t)>1e-9||Math.abs(a[1]-o)>1e-9;if(!n&&!e.moved)return;return e.moved||=n,void this._bdApply((a[0]-i.x)/Wh,(a[1]-i.y)/Wh,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const i=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,a=180*Math.atan2(e.sy-o,e.sx-i)/Math.PI;let n=e.p0.angle+(180*Math.atan2(s[1]-o,s[0]-i)/Math.PI-a);t.shiftKey||(n=5*Math.round(n/5)),n=Ue(n);const r=Math.abs(n-e.p0.angle)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,n)}const o=Qe(e.rect0,e.sgx,e.sgy,s[0],s[1],!t.shiftKey,this.host._gridPitch,Math.min(i.w,i.h)*Rt),a=o.w/Math.max(1e-9,i.w),n=o.h/Math.max(1e-9,i.h),r=Math.abs(a-e.p0.sx)>1e-9||Math.abs(n-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!r&&!e.moved)return;e.moved||=r;const l=Xe(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-i.x)/Wh,(l[1]-i.y)/Wh,a,n,e.p0.angle)}_bdReset(){const t=this.host._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this.host._bdDrag=null,this._recordGeometry(this.host._t("history.backdrop_transform"),e),this._saveConfig(),this.host._showToast(this.host._t("decor.backdrop_reset_done")),this.host.requestUpdate()}_bdUp(){Ro(this.host,"backdrop");const t=this.host._bdDrag;this.host._bdDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.backdrop_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_renderBackdropFrame(t){const e=this.host._bdRect;if(!this.host._bdActive||!e)return d;const s=.02*Math.max(t.w,t.h),i=s/4,o=e.x+e.w/2,a=e.y+e.h/2,n=Ue(e.angle),r=2.2*s;return f` + + + {t.stopPropagation(),t.preventDefault(),this._bdStart(t,void 0,!0)}}> + + ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,o,a])=>{const n=t<0?e.x:e.x+e.w,r=o<0?e.y:e.y+e.h;return f`{e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} + `}_renderEditorGroupLauncher(t){return this.host._editorSecondary.renderGroupLauncher(t,this.host._editorToolbarGroups,this.host._editorSecondaryCopy)}_runEditorContext(t,e){return this.host._editorSecondary.runContext(t,this.host._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this.host._editorSecondary.renderGroupModel(t,this.host._editorSecondaryContextId,this.host._editorSecondaryCopy)}_renderDrawWallControl(){const t="column"===this.host._tool?1:0;return p``}_renderPlanSecondary(){const t=this.host._editorSecondaryContextId,e=this.host._physicalSel;if(e){if(!("partition"===e.kind?!!this.host._curSpaceCfg.partitions?.some(t=>t.id===e.id):"column"===e.kind?!!this.host._curSpaceCfg.wall_columns?.some(t=>t.id===e.id):!!this.host._curSpaceCfg.room_drafts?.some(t=>t.id===e.id)))return null;const s="partition"===e.kind?this.host._t("markup.partition"):"column"===e.kind?this.host._t("markup.column"):this.host._t("markup.add");return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` + + `}}const s="draw"===this.host._tool||"column"===this.host._tool,i="column"===this.host._tool?"markup.hint_column":"resize"===this.host._tool?"markup.hint_resize":"wallthick"===this.host._tool?"markup.hint_wallthick":null,o="draw"===this.host._tool?this.host._t(this.host._path.length?"markup.hint_points":"markup.hint_start",this.host._path.length?{n:this.host._path.length}:void 0):"";if(!s&&!i&&!o)return null;const a="draw"===this.host._tool&&this.host._path.length>0;return{contextId:t,kind:a?"operation":"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("draw"===this.host._tool?"markup.add":"column"===this.host._tool?"markup.column":"resize"===this.host._tool?"markup.resize":"markup.wallthick")}),content:p` + ${s?this._renderDrawWallControl():d} + ${o?p`${o}`:d} + ${i?p`${this.host._t(i)}`:d} + ${"draw"===this.host._tool&&this.host._path.length?p``:d}`}}_renderDecorSecondary(){const t=this.host._editorSecondaryContextId;if("furniture"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.furniture")}),launcherId:"furniture",dismissPolicy:this.host._furnPalette?"stay-open-on-canvas":"outside",dismiss:()=>{"furniture"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._furnPalette=null,this._furnShiftDetach(),this.host._furnCategory=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderFurnPalette()};if("image"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.image")}),launcherId:"image",dismissPolicy:this.host._decorImagePalette?"stay-open-on-canvas":"outside",dismiss:()=>{"image"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._decorImagePalette=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderDecorImagePalette()};const e=this.host._dtSel;if(e){const s=this.host._t(`decor.${e.kind}`);return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` + + `}}const s="line"===this.host._decorTool||"rect"===this.host._decorTool||"ellipse"===this.host._decorTool,i="rect"===this.host._decorTool||"ellipse"===this.host._decorTool,o="backdrop"===this.host._decorTool&&!!this.host._bdRect;return s||o?{contextId:t,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t(s?`decor.${this.host._decorTool}`:"decor.backdrop")}),content:p` + ${s?p` + this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> + + ${i?p` + this._updateDecorStyle({...this.host._decorStyle,fillColor:t.detail.color,fillOpacity:t.detail.opacity})}>`:d} + `:d} + ${o?p`${this.host._t("decor.backdrop_hint")}`:d}`}:null}_withBackdropReset(t){if(!this.host._bdMoved||this.host._bdDrag||"palette"===t?.kind)return t;const e=this.host._editorSecondaryContextId,s=p``;return t?{...t,kind:"selection"===t.kind?"mixed":t.kind,content:p`${t.content}${s}`}:{contextId:e,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("decor.backdrop")}),content:s}}_renderEditorSecondary(){if(!this.host._editing)return d;const t=this.host._editorSecondary.activeGroup(this.host._editorToolbarGroups),e=t?this._renderEditorGroupModel(t):"plan"===this.host._mode?this._renderPlanSecondary():"decor"===this.host._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this.host._editorSecondary.render(e,this.host._editorSecondaryDialogBlocked)}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this.host._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],...this.host._haDecorAssetsApi===ns?[["image","mdi:image-plus-outline","decor.image"]]:[],["erase","mdi:eraser","decor.erase"]],e=this.host._geometryHistory.undoName,s=this.host._geometryHistory.redoName;return p`
    +
    + ${t.map(([t,e,s])=>p``)} + ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} + this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> + + + +
    +
    + +
    +
    `}_renderDecorEraseConfirm(){const t=this.host._decorEraseConfirm,e=this.host._t(`decor.${t.kind}`);return p`this.host._decorEraseConfirm=null}> +

    ${this.host._t("confirm.erase_decor",{kind:e})}

    +
    + + + +
    +
    `}_renderDecorTextDialog(){const t=this.host._decorTextDialog,e=(t.pickerEntity||"").trim(),s=e?this.host.hass?.states?.[e]:null;return p`this.host._decorTextDialog=null}> +
    + + ${""} + + this.host._decorTextDialog={...t,...e.detail}}> + +
    this.host._decorTextDialog={...t,sizeCm:this.host._decorTextCm(Number(e.target.value))}} /> + ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    + + this.host._decorTextDialog={...t,angle:e.target.value}} /> + + + this.host._decorTextDialog={...t,pickerEntity:e.target.value}} /> + + ${Object.keys(this.host.hass?.states||{}).map(t=>p``)} + + ${e?p` + + + `:d} +
    +
    + + + +
    +
    `}_renderDecorShapeDialog(){return this._decorImages.renderShapeDialog()}_renderBackdropDialog(){const t=this.host._backdropDialog,e=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`this.host._backdropDialog=null}> +
    + +
    this.host._backdropDialog={...t,widthCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ×this.host._backdropDialog={...t,heightCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ${e}
    + + this.host._backdropDialog={...t,angle:e.target.value}} /> +
    +
    + + +
    +
    `}_cssPxToRender(t){const e=this.host._stageEl,s=this.host._viewOr(this.host._baseVb());return e?.clientWidth&&e.clientHeight?Math.max(s.w/e.clientWidth,s.h/e.clientHeight)*t:this.host._gridPitch/8*t}_deleteRoomClick(t){const e=this.host._spaceModel();if(!e)return;const s=[...e.rooms].reverse().find(e=>this._pointInRoom(t,e));s?s.id&&(this.host._roomDeleteDialog={roomId:s.id,name:s.name}):this.host._showToast(this.host._t("toast.delete_room_pick"))}_wallThickHit(t){const e=this.host._spaceModel();if(!e)return null;const s=6*this.host._gridPitch,i=this.host._openCuts();let o=null;const a=(t,e,i)=>{e>s||(!o||e100)return void this._showPhysicalRange(100);if("room"!==e.source.kind){const t=this._geometrySnapshot();if("partition"===e.source.kind){const t=(s.partitions||[]).find(t=>t.id===e.source.id);if(!t)return;if(0===n&&Qt(s.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));t.cm=n}else{const t=(s.room_drafts||[]).find(t=>t.id===e.source.id),i=t?.segments?.[e.source.segment];if(!i)return;i.cm=n}this.host._wallDialog=null;return this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),t)&&this.host._showToast(this.host._t("toast.wallthick_set")),void this.host.requestUpdate()}const r=this._geometrySnapshot(),l=n>0?n:null;if(0===n){const i=.02*this.host._gridPitch,o=[e.a[0],e.a[1],e.b[0],e.b[1]],a=(s.rooms||[]).find(t=>t.id===e.roomId),n=new Set(t&&a&&Array.isArray(a.wall_ids)?a.wall_ids:(s.wall_segments||[]).filter(t=>{const e=[Number(t.a?.[0])*Wh,Number(t.a?.[1])*Wh],s=[Number(t.b?.[0])*Wh,Number(t.b?.[1])*Wh];return st(e,o)<=i&&st(s,o)<=i}).map(t=>String(t.id))),r=(s.openings||[]).some(t=>{if("partition"===t.host?.kind)return!1;if("wall"===t.host?.kind&&n.has(t.host.id))return!0;if(t.host)return!1;const e=[Number(t.x)*Wh,Number(t.y)*Wh];return e.every(Number.isFinite)&&st(e,o)<=i});if(r)return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"))}let h,c=this.host._openCuts();if(n>0&&c.length){const o=[];if(t&&e.roomId){const t=(s.rooms||[]).find(t=>t.id===e.roomId),a=new Set(Array.isArray(t?.wall_ids)?t.wall_ids:[]);for(const t of s.wall_segments||[])a.has(t.id)&&o.push([Number(t.a?.[0])*Wh,Number(t.a?.[1])*Wh,Number(t.b?.[0])*Wh,Number(t.b?.[1])*Wh]);if(!o.length){const t=i.rooms.find(t=>t.id===e.roomId),s=t?at(t):null;for(let t=0;s&&t!o.some(e=>st([t[0],t[1]],e)<=a&&st([t[2],t[3]],e)<=a&&st([e[0],e[1]],t)<=a&&st([e[2],e[3]],t)<=a))}h=t&&e.roomId?rs(s.walls,i.rooms,e.roomId,l,this.host._wallKeyPitch,c,Wh):pt(s.walls,e.a,e.b,l,this.host._wallKeyPitch,Wh),h=this.host._normalizeWalls(h,c);for(const t of s.wall_segments||[])t.cm=St(h,t.a,t.b,W,1);h.length?s.walls=h:delete s.walls,this.host._wallDialog=null,this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),r)&&this.host._showToast(this.host._t(0===n?"toast.wallthick_cleared":"toast.wallthick_set")),this.host.requestUpdate()}_wallHatchDefs(t){if(!this.host._spaceWalls.length&&!this.host._physicalBodiesR().length&&!this.host._markup)return f``;const e=w(this.host._cellCm);return f` + + + + `}_renderWallThickUi(){const t=this.host._wallThickHover;return t&&t.d?f``:f``}_renderWallThickDialog(){const t=this.host._wallDialog;return t?p`
    t.stopPropagation()}> +
    + + {this.host._wallDialog={...t,value:e.target.value}}} + @keydown=${t=>{"Enter"===t.key&&(t.preventDefault(),this._wallThickApply(!1))}} /> + ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")} +
    +
    + ${"room"===t.source.kind?p``:d} + + +
    +
    `:p``}_openingAt(t){if(!this.host._openingsR.length)return null;const e=this.host._openingsR.flatMap(e=>{const s=e.angle*Math.PI/180,i=t[0]-e.rx,o=t[1]-e.ry,a=i*Math.cos(s)+o*Math.sin(s);return Math.abs(a)>e.rlen/2+k(12,this.host._cellCm)?[]:[{o:e,localY:-i*Math.sin(s)+o*Math.cos(s)}]});if(!e.length)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i).value;return e.find(({o:t,localY:e})=>{const s="gate"===t.type?!t.flip_v:t.flip_v,i=t.partitionHost||this.host._spaceWalls.length||"gate"===t.type?this.host._openingFace(t,o,!!s):{ox:0,oy:0,cm:0,side:-1},a=ls({type:t.type,length:t.rlen,angle:t.angle,amount:this.host._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:i});return Math.abs(e)<=a.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this.host._openingPreset;if("opening"!==this.host._tool||!e)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i),a=`${o.key}|partitions:${this.host._cfgEpoch}`;this.host._openingPlacementIntervalsCache&&this.host._openingPlacementIntervalsCache.key===a||(this.host._openingPlacementIntervalsCache={key:a,value:[...lt(s.rooms,this.host._spaceWalls,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh),...hs(s.partitions,this.host._cellCm,this.host._gridPitch)]}),this.host._openingDimensionContextCache&&this.host._openingDimensionContextCache.key===a||(this.host._openingDimensionContextCache={key:a,value:Dh({rooms:s.rooms,walls:this.host._spaceWalls,openCuts:i,partitions:s.partitions,roomOpenings:this.host._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(s),pitch:this.host._wallKeyPitch,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,coordScale:Wh,epsilon:2e-4*this.host._gridPitch})});const n=cs({pointer:[t[0],t[1]],preset:e,geometryRevision:this.host._cfgEpoch,renderedLength:this.host._cmToUnits(e.lengthCm),intervals:this.host._openingPlacementIntervalsCache.value,baseTolerance:1.5*this.host._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this.host._pointerModality.modality||"pen"===this.host._pointerModality.modality?10:6),gridStep:this.host._gridPitch});this.host._openingJambBlockCm=n.jambBlockedTarget?n.jambBlockedTarget.physicalHalfWidth/this.host._gridPitch*this.host._cellCm:null;const r=n.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let h;if(r.host){const t=nt({id:"preview",type:r.type,x:r.x/Wh,y:r.y/Wh,angle:r.angle,length:r.renderedLength/Wh,host:r.host},s.partitions,Wh,this.host._cellCm,this.host._gridPitch).resolved;h=t?ds(t,l):{ox:0,oy:0,cm:0,side:-1}}else h=ps(o.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const c="mi"===this.host.hass?.config?.unit_system?.length,d=Ah(r,this.host._openingDimensionContextCache.value).map(t=>({x:t.label[0],y:t.label[1],text:Te(t.distance/this.host._gridPitch*this.host._cellCm,c),dimension:t}));return{...r,face:h,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(t){this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingPreset=us(t,++this.host._openingPresetRevision),this.host._openingHoverCandidate=null,this.host._cursorPt=null)}_clearOpeningPlacement(t){this.host._openingHoverCandidate=null,this.host._openingJambBlockCm=null,t&&(this.host._openingPreset=null,this.host._openingRebindId=null)}_openingClick(t){const e=this.host._spaceModel();if(!e)return;const s=this.host._openingRebindId?null:this._openingAt(t);if(s)return void this._editOpening(s);const i=this.host._openingPreset;if(!i)return;const o=this.host._openingHoverCandidate,a=o&&_s(o,[t[0],t[1]],i.revision,this.host._cfgEpoch)?o:this._resolveOpeningPlacement(t);if(!a){if(null!=this.host._openingJambBlockCm)return void this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Te(this.host._openingJambBlockCm,this.host._imperial)}));const s=1.5*this.host._gridPitch,i=it(t,e.rooms,s);return i&&ms(i.x,i.y,i.angle,this.host._openCuts(),s)?void this.host._showToast(this.host._t("toast.opening_on_zero_wall")):void this.host._showToast(this.host._t("toast.opening_no_wall"))}const n=this.host._openingRebindId?this.host._curSpaceCfg?.openings?.find(t=>t.id===this.host._openingRebindId):null;this.host._openingDialog={...n?{id:n.id,type:n.type,lengthCm:Math.round(n.length*Wh/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:n.contact||"",lock:n.lock||"",invert:!!n.invert,flipH:!!n.flip_h,flipV:!!n.flip_v}:{type:a.type,lengthCm:a.lengthCm,contact:"",lock:"",invert:!1,flipH:a.flipH,flipV:a.flipV},...a.host?{host:a.host}:{},x:a.x,y:a.y,angle:a.angle},this.host._openingRebindId=null,this.host._openingHoverCandidate=null,this.host._cursorPt=null}_editOpening(t){this.host._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,..."partition"===t.host?.kind?{host:{...t.host}}:{},x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this.host._mode&&this.host._spaceModel()&&"resize"!==this.host._tool){t.preventDefault(),t.stopPropagation();try{Gh(t)}catch{}this.host._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){zo(this.host,"opening",()=>this._opPointerMoveNow(t,e))}_opPointerMoveNow(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this.host._opDrag.sx)+Math.abs(t.clientY-this.host._opDrag.sy)<=3)return;const s=this.host._spaceModel();if(!s)return;const i=this._svgPoint(t),o=this.host._curSpaceCfg,a=o?.openings?.find(t=>t.id===e.id);if(!a)return;if("partition"===a.host?.kind){const t=s.partitions.find(t=>t.id===a.host.id);if(!t)return;const e=t.b[0]-t.a[0],o=t.b[1]-t.a[1],n=Math.hypot(e,o);if(!(n>1e-9))return;const r=e/n,l=o/n;if(Math.abs((i[0]-t.a[0])*l-(i[1]-t.a[1])*r)>4*this.host._gridPitch)return;const h=a.length*Wh/2+dt(t,this.host._cellCm,this.host._gridPitch);let c=(i[0]-t.a[0])*r+(i[1]-t.a[1])*l;if(c=Math.round(c/this.host._gridPitch)*this.host._gridPitch,c=Math.max(h,Math.min(n-h,c)),n<2*h-1e-9)return;const d=c/n,p=(t.a[0]+r*c)/Wh,u=(t.a[1]+l*c)/this.host._spaceH;let _=180*Math.atan2(o,e)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=a.host.t!==d||a.x!==p||a.y!==u||a.angle!==_;return this.host._opDrag.moved=!0,m&&(this.host._opDrag.dirty=!0),a.host={...a.host,t:d},a.x=p,a.y=u,a.angle=_,this.host._opMeasure=null,m&&this.host._cfgEpoch++,void this.host.requestUpdate()}const n=it(i,s.rooms,4*this.host._gridPitch);if(!n)return;this.host._opDrag.moved=!0;const r=this._opRuler(n,a.length*Wh);this.host._opMeasure=r.measure;const l=r.x/Wh,h=r.y/this.host._spaceH,c=a.x!==l||a.y!==h||a.angle!==n.angle;if(c&&(this.host._opDrag.dirty=!0),a.x=l,a.y=h,a.angle=n.angle,Number(this.host._serverCfg?.model_version||0)>=9){const t=Vl(a,o?.wall_segments||[]);t?a.host=t:delete a.host}c&&this.host._cfgEpoch++,this.host.requestUpdate()}_opRuler(t,e){const s=this.host._spaceModel()?.rooms||[],i=this.host._gridPitch/2;let o=t.x,a=t.y,n=gs([o,a],t.angle,e,s,i);if(n&&n.centered&&(o!==n.wallCenter[0]||a!==n.wallCenter[1]))[o,a]=n.wallCenter,n=gs([o,a],t.angle,e,s,i);else if(n){const[r,l]=n.wallA,[h,c]=n.wallB,d=h-r,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this.host._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-r)*d+(a-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=r+_/u*d,a=l+_/u*p,n=gs([o,a],t.angle,e,s,i)||n}}if(!n)return{x:o,y:a,angle:t.angle,measure:null};const r="mi"===this.host.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Te(t/this.host._gridPitch*this.host._cellCm,r)});return{x:o,y:a,angle:t.angle,measure:{labels:[l(n.sideA,n.midA),l(n.sideB,n.midB)],guide:n.centered?{x:n.wallCenter[0],y:n.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;Ro(this.host,"opening");const s=this.host._opDrag,i=s.moved;this.host._opMeasure=null,i&&s.dirty&&this._commitPhysicalGeometry(this.host._t("history.move_opening"),s.before);const o=()=>{this.host._opDrag=null,this.host.requestUpdate()};i?window.setTimeout(o,0):o()}_opClick(t,e){"plan"===this.host._mode&&"resize"===this.host._tool||(t.stopPropagation(),this.host._opDrag?.moved||"plan"===this.host._mode&&this._editOpening(e))}_saveOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=this._geometrySnapshot(),o=this.host._spaceH,a=(e.openings||[]).find(e=>e.id===t.id),n={...a||{},id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Wh,y:t.y/o,angle:t.angle,length:a&&!t.lengthTouched?a.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/Wh,...t.host?{host:{...t.host}}:{}};if("partition"===n.host?.kind){const t=fs(a,n)?_t(n,s.partitions,Wh,this.host._cellCm,this.host._gridPitch):nt(n,s.partitions,Wh,this.host._cellCm,this.host._gridPitch);if(!t.resolved){const e=s.partitions.find(t=>t.id===n.host.id);if("does-not-fit-jamb"===t.reason&&e){const t=dt(e,this.host._cellCm,this.host._gridPitch);this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Te(t/this.host._gridPitch*this.host._cellCm,this.host._imperial)}))}else this.host._showToast(this.host._t("opening.partition_orphan"));return}const i=(e.openings||[]).flatMap(t=>{if(!t.host||t.id===n.id)return[];const e=nt(t,s.partitions,Wh,this.host._cellCm,this.host._gridPitch).resolved;return e?[e]:[]});if(bs(t.resolved,i))return void this.host._showToast(this.host._t("toast.opening_no_wall"));Object.assign(n,rt(n,t.resolved,Wh))}else if(Number(this.host._serverCfg?.model_version||0)>=9){const t=Vl(n,e.wall_segments||[]);if(!t)return void this.host._showToast(this.host._t("toast.opening_no_wall"));n.host=t}else delete n.host;"passage"===t.type?(delete n.contact,delete n.lock,delete n.invert,delete n.flip_h,delete n.flip_v):(n.contact=t.contact||null,n.lock=("door"===t.type||"gate"===t.type)&&t.lock||null,n.invert=t.invert||void 0,n.flip_h="gate"!==t.type&&t.flipH||void 0,n.flip_v=t.flipV||void 0),e.openings=e.openings||[];const r=e.openings.findIndex(t=>t.id===n.id);r>=0?e.openings[r]=n:e.openings.push(n),this._commitPhysicalGeometry(this.host._t(t.id?"history.edit_opening":"history.add_opening"),i),this.host._openingDialog=null,this.host.requestUpdate()}_deleteOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg;if(!t?.id||!e?.openings)return;const s=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._commitPhysicalGeometry(this.host._t("history.delete_opening"),s),this.host._openingDialog=null,this.host.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.host.hass.states)){if(!this.host._openingEntityAvailable(e))continue;const s=e.split(".")[0];if("binary_sensor"!==s&&"cover"!==s)continue;const i=this.host.hass.states[e],o=["door","window","opening","garage_door","garage"].includes(i?.attributes?.device_class||"");("cover"!==s||o)&&t.push([e,i?.attributes?.friendly_name||e,o?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.host.hass.states).filter(t=>t.startsWith("lock.")&&this.host._openingEntityAvailable(t)).map(t=>({value:t,label:this.host.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_toggleOpeningEntityPicker(t){const e=this.host._openingDialog;if(!e)return;const s="contact"===t?!e.contactOpen:!e.lockOpen;this.host._openingDialog={...e,contactOpen:"contact"===t&&s,lockOpen:"lock"===t&&s}}_filterOpeningEntities(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contactFilter:e}:{...s,lockFilter:e})}_selectOpeningEntity(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contact:e,contactOpen:!1,contactFilter:""}:{...s,lock:e,lockOpen:!1,lockFilter:""})}_mergeClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms,i=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const o=i.id;if(!this.host._mergeSel||this.host._mergeSel===o)return void(this.host._mergeSel=this.host._mergeSel===o?null:o);const a=s.find(t=>t.id===this.host._mergeSel),n=a?at(a):null,r=at(i),l=n&&r?vs(n,r):null;if(!l)return this.host._showToast(this.host._t("toast.merge_not_adjacent")),void(this.host._mergeSel=null);this.host._mergeDialog={aId:this.host._mergeSel,bId:o,poly:l,pick:"a"},this.host._mergeSel=null}_commitMerge(){const t=this.host._mergeDialog,e=this.host._curSpaceCfg;if(!t||!e||!this.host._spaceModel())return;const s=this._geometrySnapshot(),i=this.host._spaceH,o="a"===t.pick?t.aId:t.bId,a="a"===t.pick?t.bId:t.aId,n=e.rooms.find(t=>t.id===o);if(!n)return void(this.host._mergeDialog=null);n.poly=t.poly.map(t=>[t[0]/Wh,t[1]/i]),delete n.x,delete n.y,delete n.w,delete n.h,e.rooms=e.rooms.filter(t=>t.id!==a);const r=this._commitPhysicalGeometry(this.host._t("history.merge_rooms"),s);this.host._mergeDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),r&&this.host._showToast(this.host._t("toast.rooms_merged",{name:n.name||""}))}_splitClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms;if(!this.host._splitSel){const e=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this.host._splitSel={roomId:e.id,pts:[]})}const i=s.find(t=>t.id===this.host._splitSel.roomId),o=i?at(i):null;if(!i||!o)return void(this.host._splitSel=null);const a=.02*this.host._gridPitch,n=6*this.host._gridPitch,r=ys(t,o),l=r&&$s(r,o,this.host._gridPitch)||r,h=r&&l&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?l:null,c=!!h&&ws(h,o,a),d=this.host._splitSel.pts;if(!d.length)return c?void(this.host._splitSel={...this.host._splitSel,pts:[h]}):void this.host._showToast(this.host._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return we(e,o,a)?void(this.host._splitSel={...this.host._splitSel,pts:[...d,e]}):void this.host._showToast(this.host._t("toast.split_pick_inside"))}const p=xe(o,[...d,h],a);if(!p)return void this.host._showToast(this.host._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=B(u)>=B(_)?u:_,g=m===u?_:u;this.host._pendingSplit={roomId:i.id,mainPoly:m,newPoly:g},this.host._cursorPt=null,this.host._nameSel="",this.host._areaSel="",this.host._roomDialog=!0}_markupMove(t){if(!this.host._markup)return;if("column"===this.host._tool)return void(this.host._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this.host._tool||"wallthick"===this.host._tool)return void(this.host._cursorPt=this._svgPoint(t));const e="draw"===this.host._tool&&!this.host._contourClosed,s="split"===this.host._tool&&!!this.host._splitSel?.pts?.length;if(!e&&!s)return;const i=this._svgPoint(t);if(e){const e=this._resolvePlanDrawPoint(i,t.shiftKey);if(this.host._planSnapHover={contextKey:e.contextKey,candidate:e.candidate,conflicts:e.conflicts},this._syncPlanSnapConflictMarkers(e.conflicts),this._syncPlanSnapActiveMarker(e.candidate),!this.host._path.length)return;return void(this.host._cursorPt=e.point)}this.host._cursorPt=this._snap(i)}_saveRoom(){(this.host._areaSel||this.host._nameSel.trim())&&(this.host._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(t){const e=this.host._wallFaceBatch;if(!e)return;const s=e.candidates[e.index];if(!s)return;if(!t&&(s.existing||s.repair)&&1===e.candidates.length)return this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate();const i=t?{candidate:s,create:!0,name:this.host._nameSel.trim(),area:this.host._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:s,create:!1},o=[...e.decisions,i],a=e.index+1;if(a"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,o=.02*this.host._gridPitch,a=this.host._spaceModel();if(!a)return ht;for(const e of this._planSnapGeometrySnapshot().value.segments){if("room"===e.sourceKind||st(t,[e.a[0],e.a[1],e.b[0],e.b[1]])>o)continue;if("partition"===e.sourceKind)return i(a.partitions.find(t=>t.id===e.sourceId)?.cm)??ht;const s=e.sourceId.lastIndexOf(":"),n=s>=0?e.sourceId.slice(0,s):e.sourceId,r=s>=0?Number(e.sourceId.slice(s+1)):-1;return i(a.room_drafts.find(t=>t.id===n)?.segments[r]?.cm)??ht}const n=or(Math.max(0,e.length-1),s,this.host._drawWallCm,ht);for(let s=0;s+1[...t]);if(!e?.sourceKey.startsWith("active:"))return s;const i=/:(\d+)$/.exec(e.sourceKey),o=i?Number(i[1])+("b"===e.endpoint?1:0):-1;return o>=0&&oe.key===t.sourceKey),a=i.find(e=>e.key===t.targetSourceKey);if(!o||!a)return!1;const n="a"===t.endpoint?o.a:o.b;return!(Math.hypot(n[0]-t.from[0],n[1]-t.from[1])>s)&&(!(st(t.to,[a.a[0],a.a[1],a.b[0],a.b[1]])>s)&&!function(t,e){if(!t.sourceKey.startsWith("static:partition|"))return!1;const s=t.sourceKey.slice(17).split("|")[0];return!!s&&e.some(t=>"partition"===t.host?.kind&&t.host.id===s)}(t,this.host._curSpaceCfg?.openings||[]))}_applyWallRepair(t,e){const s=this.host._curSpaceCfg;if(!s||!this._validateWallRepair(t,e.activePath))return!1;const i=e=>{e[0]=t.to[0]/Wh,e[1]=t.to[1]/Wh};if(t.sourceKey.startsWith("active:")){const o=/:(\d+)$/.exec(t.sourceKey),a=o?Number(o[1])+("b"===t.endpoint?1:0):-1;if(a<0||a>=e.activePath.length)return!1;e.activePath[a]=[...t.to];const n=(s.room_drafts||[]).find(t=>t.id===e.activeDraftId);return n?.points?.[a]&&i(n.points[a]),!0}if(!t.sourceKey.startsWith("static:"))return!1;const[o,a]=t.sourceKey.slice(7).split("|");if("partition"===o){const e=(s.partitions||[]).find(t=>t.id===a);if(!e)return!1;const o=[e.a[0]*Wh,e.a[1]*Wh],n=[e.b[0]*Wh,e.b[1]*Wh],r=Math.hypot(o[0]-t.from[0],o[1]-t.from[1])<=2e-4*this.host._gridPitch?e.a:Math.hypot(n[0]-t.from[0],n[1]-t.from[1])<=2e-4*this.host._gridPitch?e.b:null;return!!r&&(i(r),!0)}if("draft"===o){const e=a.lastIndexOf(":"),o=e>=0?a.slice(0,e):"",n=e>=0?Number(a.slice(e+1)):-1,r=(s.room_drafts||[]).find(t=>t.id===o);if(!r?.points?.[n]||!r?.points?.[n+1])return!1;const l=[n,n+1].find(e=>Math.hypot(r.points[e][0]*Wh-t.from[0],r.points[e][1]*Wh-t.from[1])<=2e-4*this.host._gridPitch);return null!=l&&(i(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const t=this.host._wallFaceBatch,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=(t,e)=>{this.host._showToast(this.host._t(t,e)),this._roomDialogCancel()},o=t.decisions.filter(t=>t.create),a=o.map(t=>t.candidate.ring),n=s.rooms;for(let t=0;ts.id!==e&&!!at(s)&&E(a[t],at(s)));if(s)return void i("toast.room_overlap",{name:s.name||""})}if((e.rooms||[]).length+o.length>400)return void i("toast.physical_limit");if(o.some(t=>t.candidate.split&&!e.rooms.some(e=>e.id===t.candidate.split.roomId)))return void i("toast.contour_cannot_close");const r=o.map(t=>t.candidate.repair).filter(t=>!!t);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],t.activePath))return void i("toast.wall_repair_changed");const l=this._activePathWithRepair(t.activePath,r[0]),h=2e-4*this.host._gridPitch,c=new Map;for(let e=0;et.candidate.atomKeys)),e=o.some(t=>t.candidate.consumeAllActive);for(const s of Mr(this._wallGraphSources(l),h)){const i=s.sourceKeys.find(t=>c.has(t));!i||e||t.has(s.key)||d.push({a:s.a,b:s.b,cm:c.get(i)})}}else d.push(...ar(l,or(l.length-1,t.activeCms,this.host._drawWallCm,ht)));if((e.partitions||[]).length+d.length>jh)return void i("toast.physical_limit");const p=o.map(t=>t.candidate.ring.map(()=>"")),u=t.activeDraftId?(e.room_drafts||[]).find(e=>e.id===t.activeDraftId):null,_=this._draftSegmentsForPath(t.activePath,u,t.activeCms),m=(t,e)=>{const s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`;return s<=i?`${s}|${i}`:`${i}|${s}`};for(let e=0;e<_.length;e++){const s=_[e]?.id;if(!s)continue;const i=t.activePath[e],a=t.activePath[e+1],n=[i[0],i[1],a[0],a[1]],r=[];o.forEach((t,e)=>t.candidate.ring.forEach((s,i)=>{const o=t.candidate.ring[(i+1)%t.candidate.ring.length];st(s,n)<=h&&st(o,n)<=h&&r.push({kind:"room",owner:e,edge:i,a:s,b:o})})),d.forEach((t,e)=>{st(t.a,n)<=h&&st(t.b,n)<=h&&r.push({kind:"partition",owner:e,edge:0,a:t.a,b:t.b})});const l=[(i[0]+a[0])/2,(i[1]+a[1])/2];r.sort((t,e)=>{const s=[t.a[0],t.a[1],t.b[0],t.b[1]],o=[e.a[0],e.a[1],e.b[0],e.b[1]];return(st(l,s)<=h?0:1)-(st(l,o)<=h?0:1)||(st(i,s)<=h?0:1)-(st(i,o)<=h?0:1)||m(t.a,t.b).localeCompare(m(e.a,e.b))});const c=r[0];if(!c)continue;const u=m(c.a,c.b);for(const t of r)m(t.a,t.b)===u&&("room"===t.kind?p[t.owner][t.edge]=s:d[t.owner].id=s)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],t))return void i("toast.wall_repair_changed");const f=o.some(t=>!!t.candidate.split),b=f?ee(s.rooms,e.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh):null,v=f&&Array.isArray(e.walls)?e.walls:null;for(const t of o){const s=t.candidate.split;if(!s)continue;const o=e.rooms.find(t=>t.id===s.roomId);if(!o)return void i("toast.contour_cannot_close");o.poly=s.mainPoly.map(t=>[t[0]/Wh,t[1]/this.host._spaceH]),delete o.x,delete o.y,delete o.w,delete o.h}const y=Date.now().toString(36),$=[];if(o.forEach((t,s)=>{const i=t.area?this.host.hass.areas[t.area]?.name:"",o={id:`r${y}-${s}`,name:t.name||i||this.host._t("room.default_name"),area:t.area||null,poly:t.candidate.ring.map(t=>[t[0]/Wh,t[1]/this.host._spaceH]),...p[s].some(Boolean)?{wall_ids:p[s]}:{},...t.settings?{settings:JSON.parse(JSON.stringify(t.settings))}:{}};e.rooms.push(o),$.push({room:o,decision:t})}),t.activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==t.activeDraftId),e.room_drafts.length||delete e.room_drafts),d.length&&(e.partitions||=[],d.forEach((t,s)=>e.partitions.push({id:t.id||`partition-${y}-${s}`,a:[t.a[0]/Wh,t.a[1]/Wh],b:[t.b[0]/Wh,t.b[1]/Wh],cm:t.cm}))),f){const t=this.host._normalizeWalls(b,this.host._openCuts());t.length?e.walls=t:v?.length?e.walls=v:delete e.walls}if($.length){this.host._cfgEpoch++;const s=this.host._openCuts(),i=this.host._spaceModel();if(!i)return;let o=e.walls;for(const{room:e,decision:a}of $){const n=a.candidate.ring,r=this._wallSourceCmAt(n[0],t.activePath,t.activeCms);o=ks(o,i.rooms,e.id,r,this.host._wallKeyPitch,s,Wh);for(const a of lt(i.rooms,o,s,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh)){if(a.roomId!==e.id||"outer"!==a.kind)continue;const s=[(a.a[0]+a.b[0])/2,(a.a[1]+a.b[1])/2];o=pt(o,a.a,a.b,this._wallSourceCmAt(s,t.activePath,t.activeCms),this.host._wallKeyPitch,Wh)}}const a=this.host._normalizeWalls(o,s);a.length?e.walls=a:delete e.walls}if(!this._commitPhysicalGeometry(this.host._t("history.wall_face_batch"),g))return;delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices();const w=new Set(o.map(t=>t.area).filter(t=>!!t));if(w.size){const t={...this.host._layout};for(const e of this.host._devices){if(!w.has(e.area||"")||e.space!==this.host._space)continue;if(this.host._layout[e.id])continue;const s=this.host._defPos[e.id];s&&(t[e.id]={s:this.host._space,x:s.x/Wh,y:s.y/Wh},this.host._dirtyPos.add(e.id))}this.host._layout=t,this.host._persistLayout()}this.host._showToast(this.host._t(o.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:o.length}))}_commitRoom(){const t=this.host._curSpaceCfg,e=this.host._spaceModel();if(!t||!e)return;const s=this._geometrySnapshot(),i=this.host._spaceH,o=!!this.host._pendingSplit,a=o?ee(e.rooms,t.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh):null,n=o&&Array.isArray(t.walls)?t.walls:null;let r;if(this.host._pendingSplit){const e=t.rooms.find(t=>t.id===this.host._pendingSplit.roomId);if(!e)return this.host._pendingSplit=null,this.host._splitSel=null,void(this.host._roomDialog=!1);e.poly=this.host._pendingSplit.mainPoly.map(t=>[t[0]/Wh,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this.host._pendingSplit.newPoly}else{if(!this.host._contourClosed)return;r=this.host._path.slice(0,-1)}const l=this.host._areaSel?this.host.hass.areas[this.host._areaSel]?.name:"",h={id:"r"+Date.now().toString(36),name:this.host._nameSel||l||this.host._t("room.default_name"),area:this.host._areaSel||null,poly:r.map(t=>[t[0]/Wh,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!o&&this.host._activeDraftId){const e=Array.isArray(t.room_drafts)?t.room_drafts.find(t=>t.id===this.host._activeDraftId):null,s=or(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,ht),i=this._draftSegmentsForPath(this.host._path,e,s);i.length===r.length&&i.every(t=>!!t.id)&&(h.wall_ids=i.map(t=>t.id))}if(t.rooms.push(h),!o&&this.host._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),o){const e=this.host._normalizeWalls(a,this.host._openCuts());e.length?t.walls=e:n?.length?t.walls=n:delete t.walls}if(!o){const e=or(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,ht),s=e[0];if(null!=s){this.host._cfgEpoch++;const i=this.host._openCuts(),o=this.host._spaceModel();if(!o)return;let a=ks(t.walls,o.rooms,h.id,s,this.host._wallKeyPitch,i,Wh);for(const t of lt(o.rooms,a,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh)){if(t.roomId!==h.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],i=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return st(s,[t[0],t[1],i[0],i[1]])<=.02*this.host._gridPitch});i>=0&&(a=pt(a,t.a,t.b,e[i],this.host._wallKeyPitch,Wh))}a=this.host._normalizeWalls(a,i),a.length?t.walls=a:delete t.walls}}const c=this._commitPhysicalGeometry(this.host._t(o?"history.split_room":"history.add_room"),s);this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._pendingSplit=null,this.host._splitSel=null;const d=this.host._areaSel;if(this.host._areaSel="",this.host._nameSel="",this.host._roomDialog=!1,this.host._regSignature="",this.host._maybeRebuildDevices(),!c)return;let p=0;if(d){const t=Wh,e={...this.host._layout};for(const s of this.host._devices){if(s.area!==d||s.space!==this.host._space)continue;if(p++,this.host._layout[s.id])continue;const i=this.host._defPos[s.id];i&&(e[s.id]={s:this.host._space,x:i.x/Wh,y:i.y/t},this.host._dirtyPos.add(s.id))}this.host._layout=e,this.host._persistLayout()}const u=this.host._model.find(t=>t.id===this.host._space)?.rooms.length||0;this.host._showToast(d?this.host._t("toast.room_saved",{n:u,added:p}):this.host._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this.host._wallRepairDiagnostic=null,this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),this.host._roomDialog=!1,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._splitSel=null,this.host._mergeSel=null,this.host._mergeDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this.host._resumeDraftBySpace[this.host._space];if(!t)return;const e=this.host._spaceModel()?.room_drafts.find(e=>e.id===t);e?(this.host._activeDraftId=t,this.host._path=e.points.map(t=>[...t]),this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.segments.map(t=>t.cm),e.id),this._clearPlanSnapHover()):delete this.host._resumeDraftBySpace[this.host._space]}_roomDialogCancel(){if(this.host._roomDialog=!1,this.host._roomEditId)return this.host._roomEditId=null,this.host._nameSel="",void(this.host._areaSel="");if(this.host._wallFaceBatch){const t=this.host._wallFaceBatch;return this.host._wallFaceBatch=null,this.host._path=t.activePath.map(t=>[...t]),this.host._draftSegmentCms=[...t.activeCms],this.host._activeDraftId=t.activeDraftId,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate()}if(this.host._pendingSplit)return this.host._pendingSplit=null,void(this.host._splitSel=null);this.host._undoPoint()}_openDeviceInbox(){this.host._deviceInboxReturn=null,this.host._deviceInbox=this.host._deviceInbox||{tab:"on_plan",search:"",showEntities:!1,onlyNew:!1,limit:100}}_closeMarkerDialog(){if(this.host._markerDialog=null,this.host._deviceInboxReturn){const t={...this.host._deviceInboxReturn};this.host._deviceInbox=t,this.host._deviceInboxReturn=null,t.anchor&&this.host.updateComplete.then(()=>requestAnimationFrame(()=>{const e=`.device-inbox-row[data-binding="${CSS.escape(t.anchor)}"]`;this.host.renderRoot.querySelector(e)?.scrollIntoView({block:"nearest"})}))}}_deviceInboxCandidates(t){return Nn({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:t,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}})}_deviceInboxRows(){const t=this.host._deviceInbox||this.host._deviceInboxReturn,e=!!t?.showEntities,s=[this.host._haRegistry.revision,this.host._cfgRev,this.host._cfgEpoch,this.host._regSignature,this.host._newSyncKey,e?1:0,this.host._showAll?1:0,l(this.host.hass,this.host._config?.language)].join("|");if(this.host._deviceInboxMemo?.key===s)return this.host._deviceInboxMemo.rows;const i=this._deviceInboxCandidates(e),o=new Set(i.map(t=>t.value));for(const t of this.host._markers)t.binding&&"virtual"!==t.binding&&o.add(t.binding);for(const t of this.host._devices)t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&o.add(`${t.bindingKind}:${t.bindingRef}`);const a=new Map;for(const t of o)a.set(t,this.host._bindingStatus(t));const n=this.host._areaToSpace,r={};for(const[t,e]of Object.entries(this.host.hass?.areas||{}))r[t]=n[t]?.room?.name||e?.name||t;for(const[t,e]of Object.entries(n))r[t]=e.room.name||r[t]||t;const h=Object.fromEntries(this.host._model.map(t=>[t.id,t.title])),c=Object.fromEntries(Object.entries(n).map(([t,e])=>[t,e.space])),d={},p=new Map;for(const[t,e]of Object.entries(this.host._fullRegistryHass.entities||{})){const s=String(e?.platform||"").trim();if(s&&(d[`entity:${t}`]=s),s&&e?.device_id){const t=p.get(e.device_id)||new Set;t.add(s),p.set(e.device_id,t)}}for(const[t,e]of p)d[`device:${t}`]=[...e].sort().join(", ");const u={};for(const[t,e]of Object.entries(this.host._fullRegistryHass.devices||{})){const s=`device:${t}`,i=p.get(t)||new Set,o=Array.isArray(e?.identifiers?.[0])?String(e.identifiers[0][0]||""):"";o&&!d[s]&&(d[s]=o);const a=[o,...i].some(t=>this.host._excluded.has(t));"service"===e?.entry_type?u[s]="service_entry":a?u[s]="excluded_integration":"Group"===e?.model?u[s]="grouped_light":/scene/i.test(e?.model||"")?u[s]="excluded_domain":(/bridge/i.test(`${e?.model||""}${e?.name||""}`)||"myheat"===o&&e?.via_device_id)&&(u[s]="represented_by_parent")}const _=function(t){const{devices:e,markers:s,candidates:i,statuses:o,newDeviceIds:a,showHiddenOnPlan:n,areaNames:r={},spaceNames:l={},spaceByArea:h={},integrationByBinding:c={},reasonByBinding:d={}}=t,p=new Map;for(const t of e)!t.virtual&&t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&p.set(`${t.bindingKind}:${t.bindingRef}`,t);const u=new Map,_=new Map;for(const t of s)t?.binding&&"virtual"!==t.binding&&(t.removed?_.set(t.binding,t):u.set(t.binding,t));const m=new Map(i.map(t=>[t.value,t])),g=new Set([...p.keys(),...u.keys(),...m.keys(),...[..._.keys()].filter(t=>m.has(t))]),f=[];for(const t of g){const e=p.get(t),s=u.get(t),i=s?void 0:_.get(t),g=m.get(t);let b;if(i&&g)b="readd";else if(!0===s?.hidden)b="hidden";else if(e||s)b="on_plan";else{if(!g)continue;b="available"}const v=e?.bindingStatus||o.get(t)||Hn,y=s||i,$=Ln(t),w=e?.area||g?.areaId||y?.area||"",k=e?.space||y?.space||h[w]||"",x=e?.name||y?.name||g?.label||t,S=e?.model||y?.model||g?.model||"",M=!!g?.parentDeviceId&&p.has(`device:${g.parentDeviceId}`),D="readd"===b?"removed":"hidden"===b?s&&En(s)?d[t]||"automatic_hidden":"manual_hidden":"on_plan"===b?s?"visible_explicit":"visible_auto":d[t]||(M?"represented_by_parent":w&&k?"available":"no_bound_room"),C=!!e&&(!e.hidden||n),P="active"===v.kind,I=C&&(P||"ha_disabled"===v.kind&&n),T=[x,S,c[t],r[w],l[k],t,g?.sub].filter(Boolean).join(" ").toLocaleLowerCase();f.push({key:t,binding:t,category:b,status:v,reason:D,deviceId:e?.id,markerId:y?.id,name:x,icon:e?.icon||y?.icon||("entity"===$?"mdi:code-braces":"mdi:devices"),model:S,integration:c[t]||"",areaId:w,areaName:r[w]||"",spaceId:k,spaceName:l[k]||"",kind:$,isNew:!!e&&a.has(e.id),searchText:T,canFind:I,canEdit:!!e||!!s,canHide:"on_plan"===b&&P,canShow:"hidden"===b&&P,canAdd:("available"===b||"readd"===b)&&P})}const b=t=>"active"===t.status.kind?1:0;return f.sort((t,e)=>t.category.localeCompare(e.category)||Number(e.isNew)-Number(t.isNew)||b(t)-b(e)||t.name.localeCompare(e.name)||t.binding.localeCompare(e.binding))}({devices:this.host._devices,markers:this.host._markers,candidates:i,statuses:a,newDeviceIds:this.host._newIds,showHiddenOnPlan:this.host._showAll,areaNames:r,spaceNames:h,spaceByArea:c,integrationByBinding:d,reasonByBinding:u});return this.host._deviceInboxMemo={key:s,rows:_},_}_deviceForInboxRow(t){const e=t.deviceId?this.host._devices.find(e=>e.id===t.deviceId):null;if(e)return e;const s=t.markerId?this.host._markers.find(e=>e.id===t.markerId):null;return!s||s.removed?null:{id:s.id,name:t.name,model:t.model,area:t.areaId,space:t.spaceId||this.host._space,hidden:!0===s.hidden||"ha_disabled"===t.status.kind,userHidden:!0===s.hidden,bindingStatus:t.status,icon:t.icon,entities:"active"===t.status.kind?t.status.enabledEntityIds:[],allEntities:t.status.allEntityIds,primary:"active"===t.status.kind?t.status.enabledEntityIds[0]:void 0,marker:s,bindingKind:t.kind,bindingRef:t.binding.slice(t.binding.indexOf(":")+1),pdfs:s.pdfs||[]}}_openInboxMarker(t,e=!1){const s=this.host._deviceInbox;if(s){if(this.host._deviceInboxReturn={...s,anchor:t.key},this.host._deviceInbox=null,!e){const e=this._deviceForInboxRow(t);return void(e?this._openMarkerDialog(e):this._closeMarkerDialog())}this._openMarkerDialog(),this.host._markerDialog?this.host._markerDialog={...this.host._markerDialog,bindingMode:"ha",binding:t.binding,bindingOpen:!1,showEntities:"entity"===t.kind,name:""}:this._closeMarkerDialog()}}async _setInboxHidden(t,e){const s=this.host._deviceInbox,i=this.host._serverCfg;if(!s||!i||s.busy||"active"!==t.status.kind)return;const o=i.markers||[],a=o.find(e=>!e.removed&&e.binding===t.binding);if(!e&&!a)return;const n=a?.id||xs(t.binding,t.markerId,()=>`m_${Date.now().toString(36)}`),r=a?{...a,hidden:e}:{id:n,binding:t.binding,hidden:!0};i.markers=[...o.filter(e=>e.id!==n&&(e.binding!==t.binding||!0===e.removed)),r],this.host._deviceInbox={...s,busy:t.key,anchor:t.key};try{await this._saveConfigNow(),this.host._regSignature="",this.host._deviceInboxMemo=null,this.host._maybeRebuildDevices(),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("device_inbox.saved"))}catch(t){this.host._serverCfg===i&&(i.markers=o),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_findInboxDevice(t){if(!t.canFind)return;const e=this._deviceForInboxRow(t);if(!e)return;if(this.host._deviceInbox=null,e.space&&e.space!==this.host._space){if(!this.host._commitSpace(e.space))return;this.host._restoreZoom()}const s=()=>{const t=this.host._devices.find(t=>t.id===e.id)||e,s=this.host._pos(t);this.host._applyView(this.host._zoom,s.x,s.y),this.host._selId=t.id,this.host.requestUpdate(),window.setTimeout(()=>{this.host._selId===t.id&&(this.host._selId=null,this.host.requestUpdate())},1500)};requestAnimationFrame(()=>requestAnimationFrame(s))}_openMarkerDialog(t){if(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null,t&&this.host._ackNewDevice(t.id),this.host._norm)if(t){const e=t.marker,s=Object.prototype.hasOwnProperty.call(e||{},"is_light"),i=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),o=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),a=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),n=Object.prototype.hasOwnProperty.call(e||{},"value_source"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),l=Object.prototype.hasOwnProperty.call(e||{},"toggle_entity"),h=Ss(e?.glow_color),c=this.host._devicePresentation(t,!0).valueBadge,d=Ms(this.host._planHass,t,this.host._devices),p=Ds(this.host._planHass,t,d);this.host._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this.host._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Is(t.marker?.display),rippleColor:D(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):1.5,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Ps(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:e?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:l,originalToggleEntity:e?.toggle_entity,tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:Cs(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:s,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:i,originalLightEntity:e?.light_entity,glowMode:null!=h?.bri?"fixed":h?"color":"auto",glowColor:h?.c||this.host._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(h?.bri??1))),glowColorDrafted:!!h,glowBrightnessDrafted:null!=h?.bri,glowTouched:!1,originalHasGlowColor:o&&(!!h||null===e?.glow_color),originalGlowColor:h||(null===e?.glow_color?null:void 0),valueBadgeEnabled:a?!0===e?.value_badge?.enabled:!!c,valueBadgeSource:e?.value_badge?.source||c?.source||p,valueBadgePosition:e?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:a,originalValueBadge:e?.value_badge,valueSource:e?.value_source||null,valueSourceTouched:!1,originalHasValueSource:n,originalValueSource:e?.value_source,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this.host._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",roomTouched:!1,hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this.host._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:1.5,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",toggleEntity:"",toggleEntityTouched:!1,originalHasToggleEntity:!1,originalToggleEntity:void 0,tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this.host._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,valueSource:null,valueSourceTouched:!1,originalHasValueSource:!1,originalValueSource:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",roomTouched:!1,hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this.host._showToast(this.host._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ts)for(const[s,i]of Object.entries(this.host.hass.states))s.startsWith(e+".")&&this.host._planEntityAvailable(s)&&t.push({value:s,label:i?.attributes?.friendly_name||s,sub:this.host._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=Nn({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:!!this.host._markerDialog?.showEntities,currentBinding:this.host._markerDialog?.binding,currentDeviceId:this.host._markerDialog?.devId,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}}),e=(this.host._markerDialog?.bindingFilter||"").toLowerCase().trim(),s=e?t.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(e)):t;return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,s=this.host._t(t);if(s!==t)return s}return this.host._errText(t)}async _pickMarkerFiles(t){const e=t.target,s=e.files?[...e.files]:[];if(e.value="",!s.length||!this.host._markerDialog)return;const i=this.host._markerDialog.uploadId||this.host._markerDialog.devId||"new",o=[];for(const t of s)try{const e=new FormData;e.append("marker_id",i),e.append("file",t,t.name);const s=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await s.json().catch(()=>({}));if(!s.ok||a.error){const t={too_large:this.host._t("err.too_large",{mb:a.max_mb||50}),bad_ext:this.host._t("err.bad_ext"),unauthorized:this.host._t("err.unauthorized")};throw new Error(t[a.error]||a.error||"HTTP "+s.status)}o.push({name:a.name||t.name,url:a.url})}catch(e){this.host._showToast(this.host._t("toast.file_failed",{name:t.name,err:this.host._errText(e)}))}o.length&&this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:[...this.host._markerDialog.pdfs,...o]},this.host._showToast(this.host._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:this.host._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const s={c:D(t.glowColor,this.host._fillColors.glow_light.c)};"fixed"===t.glowMode&&(s.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=s}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}_markerToggleEntityFields(t){return(e={touched:t.toggleEntityTouched,originalHas:t.originalHasToggleEntity,original:t.originalToggleEntity,value:t.toggleEntity}).touched?e.value?{toggle_entity:e.value}:{}:e.originalHas?{toggle_entity:e.original??null}:{};var e}async _saveMarker(){const t=this.host._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return;if("virtual"===t.binding&&!t.name.trim())return void this.host._showToast(this.host._t("toast.virtual_name_required"));if("run"===e&&!t.tapTarget)return void this.host._showToast(this.host._t("toast.run_target_required"));if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)return void this.host._showToast(this.host._t("toast.value_badge_source_required"));if("ha"===t.bindingMode){const e=this.host._bindingStatus(t.binding),s=t.devId?this.host._markers.find(e=>e.id===t.devId):null,i=!s||s.binding!==t.binding;if("active"!==e.kind&&i)return void this.host._showToast(this.host._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===s?.hidden&&!t.hideFromPlan)return this.host._markerDialog={...t,hideFromPlan:!0},void this.host._showToast(this.host._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const s=this.host._serverCfg;if(!s)return;const i=this.host._cfgRev,o=_(s);let a=JSON.parse(JSON.stringify(s));const n=a.markers||[],r=xs(t.binding,t.devId,()=>"v_"+Date.now().toString(36)),l=t.devId,h=l?this.host._devices.find(t=>t.id===l):null,c=l?n.find(t=>t.id===l):null,d=!!c&&("string"==typeof c.area&&c.area.length>0||null===c.area&&!!c.space&&!!c.room_id),p="virtual"===t.binding||t.roomTouched||d,g="virtual"===t.binding||t.roomTouched?zs(t.room):null;let f=d&&!t.roomTouched?c?.space||null:g?.space||null;const b=d&&!t.roomTouched?c?.area||null:g?.area||null,v=d&&!t.roomTouched?c?.room_id||null:g?.roomId||null,y=f||h?.space||null,$=y?this.host._spaceModelById(y):this.host._spaceModel();if(!$)return;const w=$.id;"virtual"!==t.binding||f||(f=w),this.host._markerDialog={...t,busy:!0};let k=null,x=!1;try{const c="virtual"===t.binding?[]:n.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),d=c.length>0,m=Cs(t.binding,t.controls,this.host._bindingEntities(t.binding)),g=n.find(t=>t.id===r||t.id===l)?.vacuum||null,y={id:r,vacuum:g,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:m.length?m:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Oh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};p&&(y.space=f,y.area=b,y.room_id=v);const S=h?.marker?.room_id??null,M=t.roomTouched&&null!=h&&(h.space!==f||h.area!==b||S!==v);let D=!1;const C=t.uploadId||l;if(C&&C!==r&&y.pdfs?.length)try{const t=await this.host.hass.callWS({type:"houseplan/files/migrate",from_id:C,to_id:r}),e=t?.mapping||{};y.pdfs=Rs(y.pdfs,C,r,e),D=Object.keys(e).length>0}catch(t){this.host._showToast(this.host._t("toast.files_migrate_failed",{err:this.host._errText(t)}))}a.markers=n,l&&l!==r&&(a.markers=As(a.markers,l,r)),l&&l!==r&&"derived_marker_state"===y.value_badge?.source?.kind&&y.value_badge.source.ref===`marker:${l}`&&(y.value_badge.source={kind:"derived_marker_state",ref:`marker:${r}`}),l&&l!==r&&"derived_marker_state"===y.value_source?.kind&&y.value_source.ref===`marker:${l}`&&(y.value_source={kind:"derived_marker_state",ref:`marker:${r}`}),a.markers=a.markers.filter(t=>t.id!==r&&t.id!==l&&("virtual"===y.binding||t.binding!==y.binding)),a.markers.push(y);const P=new Set(c);l&&l!==r&&P.add(l),P.delete(r),P.size&&a.settings?.marker_area_snapshot&&(a.settings={...a.settings,marker_area_snapshot:Fs(a.settings.marker_area_snapshot,P)});let I=null;const T=l?this.host._layout[l]:null,z=T?{s:T.s||h?.space||this.host._space,x:T.x,y:T.y}:l&&h&&this.host._defPos[l]?this.host._normPos(h.space,this.host._defPos[l].x,this.host._defPos[l].y):null;if(!d&&z&&z.s===w)r===l&&this.host._layout[r]&&!M||(I={s:z.s,x:z.x,y:z.y});else if(d||!this.host._layout[r]||M){let t=$.vb[0]+$.vb[2]/2,e=$.vb[1]+$.vb[3]/2;const s=v?$.rooms.find(t=>t.id===v):b?$.rooms.find(t=>t.area===b):void 0;s&&([t,e]=this.host._roomCenter(s)),I=this.host._normPos(w,t,e)}if(this.host._serverCfg!==s||this.host._cfgRev!==i||_(s)!==o)return this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),void this.host._showToast(this.host._t("toast.conflict"));if(a=this._prepareConfigCandidate(a),k=u(s,a,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=a,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.cancel(),await this._saveConfigNow(k),x=!0,I){const t=Ns(I);this.host._layout={...this.host._layout,[r]:t},this.host._noteLayoutRev(await this.host.hass.callWS({type:"houseplan/layout/update",device_id:r,pos:t}))}const R=new Set(c);l&&l!==r&&R.add(l),R.delete(r);for(const t of R)delete this.host._layout[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{});D&&C&&await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:C}).catch(()=>{}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_saved"))}catch(t){!x&&k&&(m(this.host,k,_),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteMarker(){const t=this.host._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this.host._devices.find(e=>e.id===t.devId):null,s=this.host._markers.find(e=>e.id===t.devId);if(!e&&!s)return;const i=e?"virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"":s.binding;if(!i)return;const o=t.name||this.host._t("device.fallback"),a=t.devId,n=await this.host._confirmDanger({key:"remove-marker",kind:"destructive",title:this.host._t("confirm.remove_marker_title"),message:this.host._t("confirm.remove_marker_body"),objectName:o,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._markerDialog;if(!n||!r||r.busy||r.devId!==a)return;const l=this.host._devices.find(t=>t.id===a),h=this.host._markers.find(t=>t.id===a);if(!l&&!h)return;const c=this.host._serverCfg;if(!c)return;c.markers=c.markers||[];const d=c.markers,p=l?"virtual"===l.bindingKind?"virtual":l.bindingKind&&l.bindingRef?`${l.bindingKind}:${l.bindingRef}`:"":h.binding;if(!p||p!==i)return;const u=Hs(c.markers,a,p,"virtual"===p);c.markers=Ls(u.markers,u.cleanupIds);const _=u.cleanupIds;_.size&&c.settings?.marker_area_snapshot&&(c.settings={...c.settings,marker_area_snapshot:Fs(c.settings.marker_area_snapshot,_)}),this.host._markerDialog={...r,busy:!0};try{await this._saveConfigNow();for(const t of _){delete this.host._layout[t],delete this.host._defPos[t],this.host._dirtyPos.delete(t),this.host._sentPos.delete(t);const e=this.host._activityRt.get(t);e&&clearTimeout(e.timer),this.host._activityRt.delete(t),this.host._vacRt.delete(t),delete this.host._vacSrvTrails[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this.host._deviceInboxReturn&&(this.host._deviceInboxReturn={...this.host._deviceInboxReturn,tab:"readd",anchor:p}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._infoCard?.id===a&&this.host._closeInfoCard(),this.host._selId===a&&(this.host._selId=null),this.host._drag?.id===a&&(this.host._drag=null),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_removed"))}catch(t){this.host._serverCfg===c&&(c.markers=d),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_openSpaceDialog(t,e){if(this.host._serverStorage&&this.host._serverCfg)if("edit"===t){const s=this.host._serverCfg.spaces.find(t=>t.id===e);if(!s)return;const i=Es(s),o=s.settings?.custom_fill&&"object"==typeof s.settings.custom_fill?Os(s.settings.custom_fill):null,a="none"===i.fill?{...o||Bs,a:0}:o;this.host._spaceDialog={mode:t,spaceId:e,title:s.title,planUrl:s.plan_url||null,planFile:null,source:s.plan_url?"file":"draw",showBorders:i.showBorders,showNames:i.showNames,zeroWallStyle:js(s),displayTouched:!0,hideDecor:i.hideDecor,hideOpenings:i.hideOpenings,roomColor:i.color,roomOpacity:i.opacity,fillMode:"none"===i.fill?"custom":i.fill,customFill:a,glowEnabled:i.glow,bgColor:i.bgColor,bgMode:"static"===s.settings?.bg_mode||"daynight"===s.settings?.bg_mode?s.settings.bg_mode:null,northDeg:ne({},s.settings),sunRays:"boolean"==typeof s.settings?.sun_rays?s.settings.sun_rays:null,tempMin:i.tempMin,tempMax:i.tempMax,showLqi:i.showLqi??this.host._config?.show_signal??!0,cardFontScale:i.cardFontScale,labelTemp:i.labelTemp,labelHum:i.labelHum,labelLqi:i.labelLqi,labelLight:i.labelLight,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,cellCmInput:Ws(Number(s.cell_cm)>0?Number(s.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1}}else this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...ro(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:Ks,roomOpacity:Us,fillMode:"custom",customFill:{...Bs,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:qs,tempMax:Gs,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Vs(this.host._imperial),cellCmInput:Ws(Vs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1};else this.host._showToast(this.host._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,s=e.files?.[0];if(!s||!this.host._spaceDialog)return;e.value="",this._decorAssetGuardReplace=null;const i=await no(s);if("reject"===i.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===i.kind)return void(this.host._backdropGuard=i.state);const o=await lo(s,i.ext,s.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:o})}_renderBackdropGuard(){const t=this._decorAssetGuardReplace;return null!==t?ho(this.host,()=>{},()=>{this.host._backdropGuard=null,this._decorAssetGuardReplace=null},this.host.hass,(e,s)=>this._uploadDecorImage(e,s,t),(this.host._backdropGuard?.file.size||0)<=2097152)??d:ho(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??d}_useServerPlan(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this.host._display(t);if(e){const s=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:0),s.onerror=()=>t(0),s.src=e}),i=this.host._spaceDialog;return i&&i.planUrl===t&&Number.isFinite(s)&&s>0?(this.host._spaceDialog={...i,savedAspect:s},s):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const e=this.host._spaceDialog,s=e?.saved?.find(e=>e.name===t);if(!e||!s||s.used_by.length||s.url===e.planUrl)return;const i=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),o=this.host._spaceDialog,a=o?.saved?.find(e=>e.name===t);if(i&&o&&a&&a.url===s.url&&a.modified===s.modified&&!a.used_by.length&&a.url!==o.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this.host._spaceDialog;e?.saved&&(this.host._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
    ${this.host._t("space.loading")}
    `;const e=t.saved||[];if(!e.length)return p`
    ${this.host._t("space.no_saved")}
    `;return p`
    + ${e.map(e=>{return p` +
    + +
    + ${e.name} + ${s=e.size,s>=1048576?(s/1048576).toFixed(1)+" MB":Math.round(s/1024)+" KB"}${e.used_by.length?" · "+this.host._t("space.used_by",{list:e.used_by.join(", ")}):""} +
    + + +
    `;var s})} +
    `}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const e="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const s="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let i=null;if("file"===t.source&&t.planFile){i={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:s,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!i&&"file"===t.source&&t.planUrl&&!o&&this.host._aspectJob&&(o=await this.host._aspectJob||null);const a=this.host._serverCfg;let n;if("create"===t.mode)n=co(s,t.title.trim()),a.spaces.push(n);else{if(n=a.spaces.find(t=>t.id===s),!n)throw new Error("space "+s+" is gone from the config");n.title=t.title.trim()}i?(n.plan_url=i.url,n.plan_aspect=i.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==n.plan_url&&(n.plan_url=t.planUrl,n.plan_aspect=o),"draw"===t.source&&(n.plan_url=null,n.plan_aspect=null,delete n.plan_x,delete n.plan_y,delete n.plan_scale,delete n.plan_scale_x,delete n.plan_scale_y,delete n.plan_angle),n.settings={...n.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):qs,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):Gs,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},n.zero_wall_style=t.zeroWallStyle,n.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(Eh,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(n.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length?this._openNextImport():e||this.host._importTotal>0?(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._showToast(this.host._t(e&&!this.host._importTotal?"toast.space_added_onboard":"import.done"))):(this.host._showToast("create"===t.mode?this.host._t("toast.space_added"):this.host._t("toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?this._setMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._saveNav())))}catch(t){"conflict"!==t?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this.host._serverCfg;if(!e)return;const s=e.spaces.find(e=>e.id===t.spaceId),i=po(e,this.host._layout||{},t.spaceId||""),o=1===e.spaces.length&&e.spaces[0]?.id===t.spaceId;if(i.count&&!o)return void(this.host._spaceDialog={...t,deleteBlockers:i.count});if(!s)return;const a=t.spaceId,n=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:uo(this.host._t("confirm.delete_space_body"),this.host._t("confirm.delete_space_vac_routes"),i.routeCount),objectName:s.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._spaceDialog,l=this.host._serverCfg;if(!n||!r||"edit"!==r.mode||r.busy||r.spaceId!==a||!l)return;if(!l.spaces.find(t=>t.id===a))return;const h=po(l,this.host._layout||{},a),c=1===l.spaces.length&&l.spaces[0]?.id===a;if(!h.count||c){this.host._spaceDialog={...r,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:a,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(e,s),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===a&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=po(e,this.host._layout||{},a),s=1===e.spaces.length&&e.spaces[0]?.id===a;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...r,deleteBlockers:h.count}}async _saveConfigNow(t=null){this.host._cfgEpoch++;try{await this._writeConfig(t)}catch(t){throw t?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===t?.code&&await this.host._reloadConfigOnly(),t}}_startImport(){const t=this.host._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,e.length?(this.host._importQueue=e,this.host._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();void 0!==t&&(this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...ro(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:Ks,roomOpacity:Us,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:qs,tempMax:Gs,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Vs(this.host._imperial),cellCmInput:Ws(Vs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._showToast(this.host._t("import.done")))}_renderImportDialog(){const t=this.host._importDialog,e=t.floors.filter(t=>t.checked).length;return p`this.host._importDialog=null}> +
    +
    ${this.host._t("import.hint")}
    + ${t.floors.map((e,s)=>p``)} +
    +
    + + + +
    +
    `}_supportPatch(t,e){const s=this.host._supportDialog;if(!s||s.draftId!==t)return null;const i={...s,...e};return this.host._supportDialog=i,i}async _focusSupport(t){await this.host.updateComplete,this.host.renderRoot.querySelector(t)?.focus({preventScroll:!0})}_updateSupportDraft(t,e){const s=this.host._supportDialog;if(!s||"building"===s.status||"sending"===s.status||"success"===s.status)return;const i={...s,[t]:e},o=_h({...i,attach:!1,preview:null});"message_required"===o&&"validation.message_required"===s.errorCode||"message_too_long"===o||"contact_too_long"===o?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:`validation.${o}`}):i.attach&&i.preview&&i.preview.expiresAt<=Date.now()?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:"support_preview_expired"}):this._supportPatch(s.draftId,{[t]:e,status:i.attach&&i.preview?"ready":"idle",errorCode:""})}async _discardSupportPreview(t){if(t)try{await this.host.hass.callWS({type:"houseplan/support/preview/discard",token:t})}catch{}}async _closeSupportDialog(){const t=this.host._supportDialog;if(t){if("building"===t.status||"sending"===t.status){const t=l(this.host.hass,this.host._config?.language);if(!await this.host._confirmDanger({key:"close-support-busy",kind:"warning",title:ko(t,"support.close_busy_title"),message:ko(t,"support.close_busy_body"),confirmLabel:this.host._t("btn.close"),cancelLabel:this.host._t("btn.cancel")}))return await this.host.updateComplete,void this.host.renderRoot.querySelector("#support-dialog")?.rejectClose()}clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this.host._supportDialog=null,t.preview?.token&&"success"!==t.status&&this._discardSupportPreview(t.preview.token)}}_scheduleSupportExpiry(t,e){clearTimeout(this._supportExpiryTimer);const s=Math.max(0,e.expiresAt-Date.now());this._supportExpiryTimer=window.setTimeout(()=>{const s=this.host._supportDialog;s&&s.draftId===t&&s.preview?.token===e.token&&s.attach&&"success"!==s.status&&"sending"!==s.status&&this._supportPatch(t,{status:"error",errorCode:"support_preview_expired"})},Math.min(s+20,2147483647))}_supportFacts(){const t=this.host._haRegistry;return gh({userAgent:globalThis.navigator?.userAgent||"",language:l(this.host.hass,this.host._config?.language),registryAccess:t.access,registryLastSuccess:t.lastSuccess})}_supportPreviewRequestIsCurrent(t,e){const s=this.host._supportDialog;return e===this._supportPreviewGeneration&&s?.draftId===t&&s.attach}async _buildSupportPreview(t){const e=this.host._supportDialog;if(!e||e.draftId!==t||!e.attach||!ch(this.host._haSupportApi))return;const s=++this._supportPreviewGeneration;this._supportPatch(t,{status:"building",errorCode:""});let i="";try{const e=await this.host.hass.callWS({type:"houseplan/support/preview",card_version:Fh,...this._supportFacts(),draft_id:t}),o=e&&"object"==typeof e?e:{},a=Date.now(),n="string"==typeof o.text?o.text:"",r=Number(o.size),l=Number(o.expires_in),h=Number(o.spaces),c=String(o.token||"");/^[0-9a-f]{48}$/.test(c)&&(i=c);const d=String(o.sha256||""),p=Number(o.version),u=String(o.format||""),_=(new TextEncoder).encode(n).byteLength;if(!/^[0-9a-f]{48}$/.test(c)||!/^[0-9a-f]{64}$/.test(d)||"houseplan-support-package"!==u||1!==p||!Number.isInteger(r)||r!==_||!Number.isInteger(h)||h<0||!Number.isFinite(l)||l<=0||l>600||!n.endsWith("\n"))throw{code:"support_rejected"};const m={token:c,expiresAt:a+1e3*l,size:r,sha256:d,spaces:h,format:u,version:p,text:n,preparedAt:a};if(!this._supportPreviewRequestIsCurrent(t,s))return i="",void this._discardSupportPreview(c);if(!this._supportPatch(t,{status:"ready",preview:m,errorCode:"",rawOpen:!1}))return i="",void this._discardSupportPreview(c);i="",this._scheduleSupportExpiry(t,m)}catch(e){if(i){const t=i;i="",this._discardSupportPreview(t)}if(!this._supportPreviewRequestIsCurrent(t,s))return;if(!this._supportPatch(t,{status:"error",errorCode:fh(e)}))return;this._focusSupport("#support-error")}}async _setSupportAttachment(t){const e=this.host._supportDialog;if(!e||"sending"===e.status||"success"===e.status)return;const s=e.preview?.token||"";if(!t)return this._supportPreviewGeneration+=1,clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPatch(e.draftId,{attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:""}),void(s&&this._discardSupportPreview(s));this._supportPatch(e.draftId,{attach:!0,errorCode:""}),await this._buildSupportPreview(e.draftId)}async _refreshSupportPreview(){const t=this.host._supportDialog;t&&t.attach&&"building"!==t.status&&"sending"!==t.status&&await this._buildSupportPreview(t.draftId)}_downloadSupportPreview(){const t=this.host._supportDialog?.preview;if(!t)return;const e=new Blob([t.text],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(e),i=document.createElement("a");i.href=s,i.download=`houseplan-support-${t.sha256.slice(0,12)}.json`,i.style.display="none",document.body.append(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0)}async _copySupportText(t,e){const s=l(this.host.hass,this.host._config?.language);try{await navigator.clipboard.writeText(t),this.host._showToast(ko(s,e))}catch{this.host._showToast(ko(s,"support.copy_failed"))}}async _submitSupport(){const t=this.host._supportDialog;if(!t||!ch(this.host._haSupportApi))return;const e=_h(t);if(e)return this._supportPatch(t.draftId,{status:"error",errorCode:`validation.${e}`}),void this._focusSupport("#support-message");if(!mh(t))return;const s=ph(t),i=this._supportPatch(t.draftId,{status:"sending",errorCode:"",idempotencyKey:s.idempotencyKey,submissionFingerprint:s.fingerprint});if(i)try{const e=await this.host.hass.callWS({type:"houseplan/support/submit",message:i.message.trim(),contact:i.contact.trim(),...i.attach&&i.preview?{preview_token:i.preview.token}:{},idempotency_key:i.idempotencyKey}),s=e&&"object"==typeof e&&"report_id"in e?String(e.report_id||""):"";if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(s))throw{code:"support_unavailable"};if(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,!this._supportPatch(t.draftId,{status:"success",reportId:s,errorCode:""}))return;this._focusSupport("#support-receipt")}catch(e){if(!this._supportPatch(t.draftId,{status:"error",errorCode:fh(e)}))return;this._focusSupport("#support-error")}}_supportErrorText(t){const e=l(this.host.hass,this.host._config?.language);if(t.errorCode.startsWith("validation.")){return ko(e,`support.validation.${t.errorCode.slice(11)}`)}return ko(e,`support.error.${t.errorCode||"support_unavailable"}`)}_supportMessageKeydown(t){"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this._submitSupport())}_renderSupportDialog(){const t=this.host._supportDialog,e=l(this.host.hass,this.host._config?.language),s="ru"===e?"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.ru.md":"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.md",i=ch(this.host._haSupportApi),o=(t,s)=>ko(e,t,s),a="building"===t.status||"sending"===t.status,n=_h(t),r=t.preview?Math.max(0,Math.floor((Date.now()-t.preview.preparedAt)/6e4)):0,h="error"===t.status&&!t.errorCode.startsWith("validation."),c="validation.contact_too_long"===t.errorCode,u="validation.message_required"===t.errorCode||"validation.message_too_long"===t.errorCode;return p`{this._closeSupportDialog()}}> +
    +
    +

    ${o("support.about_group")}

    +
    ${this.host._t("gs.about_version",{v:Fh})}
    + +
    +
    +

    ${o("support.guide_group")}

    + + ${o("support.guide")} +
    + ${i?p` +
    +

    ${o("support.form_group")}

    + + this._updateSupportDraft("contact",t.target.value)} /> + + + +

    ${o("support.attach_hint")}

    + ${t.attach?p`

    + + ${o("support.geometry_warning")} +

    `:d} + ${"building"===t.status?p`
    + ${o("support.building")} +
    `:d} + ${t.attach&&t.preview?p` +
    +
    ${o("support.preview_summary",{version:t.preview.version,spaces:t.preview.spaces,size:(_=t.preview.size,(Math.max(0,_)/1024).toFixed(_<10240?1:0))})}
    +
    SHA-256${t.preview.sha256}
    +
    ${o("support.prepared",{n:r})}
    +
    this._supportPatch(t.draftId,{rawOpen:e.currentTarget.open})}> + ${o("support.show_data")} + ${t.rawOpen?p``:d} +
    +
    + + +
    +
    `:d} +

    ${o("support.privacy")}

    + ${"error"===t.status?p` + `:d} + ${"error"===t.status&&t.attach&&!t.preview?p` +
    + +
    `:d} + ${h?p` +
    + ${o("support.manual_recovery")} +
    + + ${t.preview?p``:d} + Telegram + GitHub +
    +
    `:d} + ${"success"===t.status?p` +
    + ${o("support.success",{id:t.reportId})} + +
    `:d} +
    `:p` +
    + + ${o("support.update_required")} +
    `} +
    +
    + + + ${i&&"success"!==t.status?p` + `:d} +
    +
    `;var _}_preflightDiagnostics(t,e){const s=new Map((e?.spaces||[]).map(t=>[String(t?.id||""),t]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:Fh,checkedAt:(new Date).toISOString(),preflightFingerprint:t.fingerprint,failures:t.failures.map(t=>({spaceId:t.spaceId,displayName:t.displayName,reason:t.reason,detail:t.detail??null,spaceGeometryFingerprint:s.has(t.spaceId)?$e(s.get(t.spaceId)):null}))}}_reportPreflightFailure(t,e){t.ok||t.fingerprint===this.host._reportedPreflightFingerprint||(this.host._reportedPreflightFingerprint=t.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(t,e)))}_preflightVersionsDiffer(){const t=this.host._haIntegrationVersion;return"string"==typeof t&&t.length>0&&t!==Fh}async _copyPreflightDiagnostics(){const t=this.host._alignDialog?.preflight;if(!t||t.ok)return;const e=JSON.stringify(this._preflightDiagnostics(t,this.host._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(e),this.host._preflightClipboardFallback=null,this.host._showToast(this.host._t("gs.preflight_copied"))}catch{this.host._preflightClipboardFallback=e}}_checkOptimizeGeometryImpl(t){return Js(t,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)})})}_checkSpacePhysicalGeometryImpl(t,e,s){return Ys(t,e,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)}),captureWallGeometry:s?(t,e)=>s(e):void 0})}_optimizeReferenceContext(t){const e=this.host._haRegistry,s=this.host._fullRegistryHass,i={},o=(...t)=>{for(const e of t){const t=String(e||"").trim();if(t)return t}return""};for(const[t,s]of Object.entries(e.devices||{}))i[t]=o(s?.name_by_user,s?.name,s?.model);for(const[t,s]of Object.entries(e.entities||{}))i[`lg_${t}`]=o(this.host.hass?.states?.[t]?.attributes?.friendly_name,s?.name,s?.original_name);for(const t of this.host._devices)i[t.id]=o(t.name,i[t.id]);for(const t of this.host._serverCfg?.markers||[]){const e=String(t.binding||"").indexOf(":"),a=e>0?t.binding.slice(0,e):"",n=e>0?t.binding.slice(e+1):"",r="device"===a?s?.devices?.[n]:"entity"===a?s?.entities?.[n]:null;i[t.id]=o(t.name,"device"===a?r?.name_by_user:null,r?.name,"entity"===a?this.host.hass?.states?.[n]?.attributes?.friendly_name:null,i[t.id])}return{effectiveAreaByMarker:Object.fromEntries(this.host._devices.filter(t=>!t.virtual&&!!t.area).map(t=>[t.id,t.area])),ownerRoster:{authoritative:e.authoritative,deviceIds:Object.keys(e.devices||{}),entityIds:[...new Set([...Object.keys(e.entities||{}),...Object.keys(this.host.hass?.states||{})])],names:i},removeLiveMissingPositions:t}}_previewAlignDialog(t){if(!this.host._norm||!this.host._serverCfg)return;const e=this.host._serverCfg.spaces||[];let s;try{s=nh(this.host._serverCfg,this.host._layout||{},this._optimizeReferenceContext(t))}catch(t){return void this._showWallModelMigrationBlocked(t)}const i=s.changed?this.host._checkOptimizeGeometry(s.config):null;i&&this._reportPreflightFailure(i,s.config);const o=Math.ceil(10*s.report.maxShiftCm)/10,a=e.find(t=>null!=t?.id&&String(t.id)===s.report.maxSpace),n=e.length>1&&a?String(a.title||a.id):"";this.host._preflightClipboardFallback=null,this.host._alignDialog={report:s.report,config:s.config,layout:s.layout,cm:o,where:n,preflight:i,changed:s.changed,busy:!1,removeLiveMissingPositions:t}}async _runAlignToGrid(){let t=this.host._alignDialog;if(!t||t.busy||!this.host._serverCfg||!t.changed||!t.preflight?.ok)return;const e=_(t.config);if(t.preflight.fingerprint!==e){const e=this.host._checkOptimizeGeometry(t.config);if(this._reportPreflightFailure(e,t.config),t={...t,preflight:e},this.host._alignDialog=t,!e.ok)return}this._clearGeometryGesture(),this.host._alignDialog={...t,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain;const e=await this.host.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});this.host._serverCfg=t.config,this.host._layout=t.layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._cfgRev=e?.config_rev??this.host._cfgRev+1,this.host._layoutRev=e?.layout_rev??this.host._layoutRev+1,this.host._canOptimizeUndo=!!e?.can_undo,this.host._undoKind=e?.can_undo?"optimize":null,this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host._alignDialog=null,this.host._preflightClipboardFallback=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.coordsCanonicalized+t.report.latticeCoordinatesCanonicalized+t.report.wallsMerged+t.report.spansMerged+t.report.partitionsMerged+t.report.partitionsReconciled+t.report.openingsRehosted+t.report.wallsStraightened),r:String(t.report.spaceRefsRemapped+t.report.roomRefsRemapped+t.report.positionsRemapped+t.report.markersDetached+t.report.orphanRoomLabelsRemoved+t.report.orphanDevicePositionsRemoved+t.report.orphanGroupPositionsRemoved)}))}catch(t){if(this.host._alignDialog&&(this.host._alignDialog={...this.host._alignDialog,busy:!1}),"wall_model_client_outdated"===t?.code)return void this.host._showToast(this.host._t("toast.wall_model_client_outdated"));"conflict"===t?.code&&await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _undoPlanOptimization(){if(!this.host._canOptimizeUndo||this.host._optimizeUndoBusy)return;const t=this.host._undoKind;this._clearGeometryGesture(),this.host._optimizeUndoBusy=!0,this.host.requestUpdate();try{await this.host.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});const[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._serverCfg=e?.config||this.host._serverCfg,this.host._cfgRev=e?.rev??this.host._cfgRev,this.host._layout=s?.layout||this.host._layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._layoutRev=s?.rev??this.host._layoutRev,this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host.requestUpdate(),this.host._showToast(this.host._t("import"===t?"backup.import_undone":"gs.optimize_undone"))}catch(t){this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}finally{this.host._optimizeUndoBusy=!1,this.host.requestUpdate()}}async _runBackupExport(){const t=this.host._backupExportDialog;if(t&&!t.busy){this.host._backupExportDialog={...t,busy:!0,error:""};try{const e=await this.host.hass.callWS({type:"houseplan/export/create",kind:t.kind,space_id:"space"===t.kind?this.host._space:void 0,..."space"===t.kind&&t.planOnly?{plan_only:!0}:{},card_version:Fh}),s=new Blob([JSON.stringify(e.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),i=URL.createObjectURL(s),o=document.createElement("a");o.href=i,o.download=e.filename||`houseplan-${t.kind}.json`,o.style.display="none",document.body.append(o),o.click(),o.remove(),window.setTimeout(()=>URL.revokeObjectURL(i),0),this.host._backupExportDialog=null,this.host._showToast(this.host._t("backup.export_done"))}catch(t){this.host._backupExportDialog&&(this.host._backupExportDialog={...this.host._backupExportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _pickBackupImport(t){const e=t.target,s=e.files?.[0];if(e.value="",s){this.host._settingsDialog=null,this.host._backupImportDialog={filename:s.name,size:s.size,token:"",preview:null,expectedConfigRev:this.host._cfgRev,expectedLayoutRev:this.host._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain,this.host._persistLayout.pending()&&this.host._persistLayout.flush();const t="/api/houseplan/import/preview?duplicate_policy=skip",e={method:"POST",body:s,headers:{"content-type":"application/json"}},i=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth(t,e):await fetch(t,{...e,headers:{...e.headers,...this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error)throw Object.assign(new Error(o.message||o.error||`HTTP ${i.status}`),{code:o.error});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,token:o.token,preview:o.preview,expectedConfigRev:o.expected_config_rev,expectedLayoutRev:o.expected_layout_rev,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _setBackupDuplicatePolicy(t){const e=this.host._backupImportDialog;if(e&&!e.busy&&e.token&&t!==e.duplicatePolicy){this.host._backupImportDialog={...e,duplicatePolicy:t,busy:!0,error:""};try{const s=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:t});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,preview:{...e.preview,...s.preview},expectedConfigRev:s.expected_config_rev,expectedLayoutRev:s.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,duplicatePolicy:e.duplicatePolicy,preview:e.preview,confirmMissing:e.confirmMissing,busy:!1,error:this._backupErrorText(t)})}}}async _applyBackupImport(){const t=this.host._backupImportDialog;if(!t||t.busy||!t.token||!t.preview)return;if(t.preview.confirmation_required&&!t.confirmMissing)return;this.host._backupImportDialog={...t,busy:!0,error:""};const e=this.host._space;try{const s=await this.host.hass.callWS({type:"houseplan/import/apply",token:t.token,expected_config_rev:t.expectedConfigRev,expected_layout_rev:t.expectedLayoutRev,duplicate_policy:t.duplicatePolicy,confirm_missing_content:t.confirmMissing}),[i,o]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(i,o),this.host._geometryHistory.clear(),this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._defPos={},this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._visibleDeviceSnapshot=null,this.host._candidateDeviceSnapshot=null,this.host._stagedDeviceSnapshotToken=-1,this.host._capturedSnapshotConfigEpoch=-1,this.host._regSignature="",this.host._signer.invalidate(this.host.hass),this.host._resign(),this.host._maybeRebuildDevices();const a=this.host._serverCfg?.spaces||[],n="space"===s.kind&&s.space_id?s.space_id:a.some(t=>t.id===e)?e:a[0]?.id||this.host._space;this.host._hasFixedFloor?this.host._adoptInitialSpace(this.host._model,!0):this.host._commitSpace(n),this.host._backupImportDialog=null,this.host._cacheSnapshot(),this.host.requestUpdate();const r="space"===s.kind?t.preview.counts:s.counts;this.host._showToast(this.host._t("space"===s.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(s.repaired_target_refs||0)}))}catch(t){if("conflict"===t?.code&&this.host._backupImportDialog?.token)try{const t=this.host._backupImportDialog,e=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:t.duplicatePolicy});return void(this.host._backupImportDialog={...t,preview:{...t.preview,...e.preview},expectedConfigRev:e.expected_config_rev,expectedLayoutRev:e.expected_layout_rev,confirmMissing:!1,busy:!1,error:this.host._t("backup.revalidated")})}catch(e){t=e}this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}_renderBackupExportDialog(){const t=this.host._backupExportDialog,e=(this.host._serverCfg?.spaces||[]).find(t=>t.id===this.host._space);return p`this.host._backupExportDialog=null}> +
    +
    ${this.host._t("backup.export_hint")}
    + + + ${"space"===t.kind&&e?p``:d} +
    ${this.host._t("backup.privacy_warning")}
    + ${t.error?p``:d} +
    +
    + + + +
    +
    `}_renderBackupImportDialog(){const t=this.host._backupImportDialog,e=t.preview,s=(e?.content||[]).filter(t=>"decor_asset"===t.kind),i=new Set(s.map(t=>t.url)).size,o=s.filter(t=>"missing_preserved"===t.state),a=new Set(o.map(t=>t.url)).size,n=e?.counts||{},r=e?.reference_report||{},l=t=>Object.values(t||{}).reduce((t,e)=>t+(Number(e)||0),0),h=[["incoming_remapped",l(r.remapped?.incoming)],["target_repaired",l(r.remapped?.target)],["preserved_unresolved",l(r.preservedUnresolved)],["collisions",l(r.collisions)],["dropped_links",l(r.droppedIncomingLinks)],["bounded_lineages",Number(r.boundedLineages)||0]].filter(([,t])=>t>0);return p`this.host._backupImportDialog=null}> +
    +
    ${t.filename}${(t.size/1024).toFixed(1)} KB
    + ${t.busy&&!e?p`
    ${this.host._t("backup.reading")}
    `:d} + ${t.error?p``:d} + ${e?p` +
    + ${this.host._t("full"===e.kind?"backup.full":"backup.current_space")} + ${e.plan_only?p`${this.host._t("backup.plan_only_preview")}`:d} + ${this.host._t("same"===e.source?"backup.same_source":"backup.foreign_source")} + ${this.host._t("backup.created",{value:e.created_at||"—"})} + ${this.host._t("backup.versions",{card:e.card_version||"—",integration:e.integration_version||"—",model:String(e.model_version??"—")})} +
    +
    + ${this.host._t("backup.count_spaces",{n:"full"===e.kind?`${e.current_counts?.spaces||0} → ${n.spaces||0}`:String(n.spaces||0)})} + ${this.host._t("backup.count_rooms",{n:"full"===e.kind?`${e.current_counts?.rooms||0} → ${n.rooms||0}`:String(n.rooms||0)})} + ${this.host._t("backup.count_walls",{n:"full"===e.kind?`${e.current_counts?.walls||0} → ${n.walls||0}`:String(n.walls||0)})} + ${this.host._t("backup.count_openings",{n:"full"===e.kind?`${e.current_counts?.openings||0} → ${n.openings||0}`:String(n.openings||0)})} + ${this.host._t("backup.count_decor",{n:"full"===e.kind?`${e.current_counts?.decor||0} → ${n.decor||0}`:String(n.decor||0)})} + ${this.host._t("backup.count_markers",{n:"full"===e.kind?`${e.current_counts?.markers||0} → ${n.markers||0}`:String(n.markers||0)})} + ${this.host._t("backup.count_layout",{n:"full"===e.kind?`${e.current_counts?.layout||0} → ${n.layout||0}`:String(n.layout||0)})} +
    + ${e.bindings?p`
    ${this.host._t("backup.bindings",{device:String(e.bindings.device||0),entity:String(e.bindings.entity||0),virtual:String(e.bindings.virtual||0),legacy:String(e.legacy_positions||0)})}
    ${this.host._t("backup.binding_status",{active:String(e.bindings.active||0),disabled:String(e.bindings.disabled||0),missing:String(e.bindings.missing||0)})}
    `:d} + ${e.missing_areas?.length?p`
    ${this.host._t("backup.missing_areas",{areas:e.missing_areas.join(", ")})}
    `:d} + ${e.dropped_marker_links?p`
    ${this.host._t("backup.dropped_marker_links",{n:String(e.dropped_marker_links)})}
    `:d} + ${e.repaired_target_refs?p`
    ${this.host._t("backup.repaired_target_refs",{n:String(e.repaired_target_refs)})}
    `:d} + ${e.preserved_unresolved_refs?p` +
    ${this.host._t("backup.preserved_unresolved_refs",{n:String(e.preserved_unresolved_refs)})}
    ${this.host._t("backup.preserved_unresolved_hint")}
    + `:d} + ${h.length?p`
    + ${this.host._t("backup.import_details")} +
    + ${h.map(([t,e])=>p`${this.host._t(`backup.import_detail.${t}`,{n:String(e)})}`)} + ${(r.examples||[]).slice(0,8).map(t=>p` + ${t.owner} → ${t.reference} + `)} +
    +
    `:d} + ${"full"===e.kind?p` +
    ${this.host._t("backup.replace_warning")}
    + ${"foreign"===e.source?p`
    ${this.host._t("backup.foreign_bookkeeping")}
    `:d}`:p` +
    ${this.host._t("backup.final_name")}${e.space_title}
    +
    ${this.host._t("backup.target_settings")}
    + ${e.duplicates?p`
    ${this.host._t("backup.duplicates",{n:String(e.duplicates)})} + + +
    `:d}`} + ${e.content?.length?p`
    + ${this.host._t("backup.content")} + ${s.length?p`${this.host._t("backup.decor_images_summary",{assets:i,objects:s.length,missing:a})}`:d} + ${e.content.filter(t=>"decor_asset"!==t.kind).map(t=>p`${t.url} · ${this.host._t("available"===t.state?"backup.content_available":"external"===t.state?"backup.content_external":"backup.content_detach_required")}`)} +
    `:d} + ${e.confirmation_required?p``:d} + `:d} +
    +
    + + + ${e?p``:d} +
    +
    `}_setFillColor(t,e){const s=this.host._settingsDialog;this.host._settingsDialog={...s,colors:{...s.colors,[t]:{...s.colors[t],...e}}}}_updateDecorStyle(t){this.host._decorStyle=t,null!==this._decorStylePersistTimer&&window.clearTimeout(this._decorStylePersistTimer),this._decorStylePersistTimer=window.setTimeout(()=>{this._decorStylePersistTimer=null,this._persistDecorStyle()},1e3)}_persistDecorStyle(){const t=this.host._serverCfg;if(!t)return;const e=Xs(this.host._decorStyle),s={...t.settings},i=JSON.stringify(s.decor_default_style??null);e?s.decor_default_style=e:delete s.decor_default_style,JSON.stringify(s.decor_default_style??null)!==i&&(this.host._serverCfg={...t,settings:s},this._saveConfig())}async _saveSettingsDialog(){const t=this.host._settingsDialog;if(!t||t.busy)return;this.host._settingsDialog={...t,busy:!0};let e=null;try{const s=this.host._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Zs);let o={...s.settings};i?delete o.fill_colors:o.fill_colors=t.colors;const a=this.host._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(a)&&a>0&&300!==Math.round(a)?o.glow_radius_cm=Math.round(a):delete o.glow_radius_cm,t.bgColor?o.bg_color=t.bgColor:delete o.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?o.north_deg=t.northDeg:delete o.north_deg,o.bg_mode=t.bgMode,t.sunRays?o.sun_rays=!0:delete o.sun_rays,t.showRoomTooltip?delete o.show_room_tooltip:o.show_room_tooltip=!1,o=Qs(o,t.zigbeeTopology),delete o.weather_entity;const n={...s,settings:o};e=u(s,n,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=n,await this._saveConfigNow(e),!t.showRoomTooltip&&this.host._tip?.room&&(this.host._tip=null),this.host._settingsDialog=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.saved"))}catch(t){e&&m(this.host,e,_),this.host._settingsDialog&&(this.host._settingsDialog={...this.host._settingsDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_boolInput(t,e,s=!1){const i=t=>e(!!t.target.checked);return customElements.get("ha-switch")?p``:p``}_rangeInput(t,e,s,i,o,a=!1,n){const r=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?p``:p``}_renderColorRow(t,e){const s=this.host._settingsDialog.colors[t];return p`
    + {this._setFillColor(t,{c:e.detail.color,a:e.detail.opacity})}}> +
    `}_renderAlignDialog(){const t=this.host._alignDialog,e=t.report,s=t.changed&&!t.preflight?.ok,i=t.preflight?.failures||[],o=i.slice(0,3).map(t=>t.displayName),a=o.length?o.join(", "):this.host._t("gs.align_preflight_space",{n:"1"}),n=Math.max(0,i.length-o.length),r=n?this.host._t("gs.align_preflight_more",{n:String(n)}):"",l=e.spaceRefsRemapped+e.roomRefsRemapped+e.positionsRemapped+e.markersDetached,h=e.migrated+e.canonicalized+e.coordsCanonicalized+e.wallSegmentsMigrated+e.wallsMerged+e.spansMerged+e.partitionsMerged+e.partitionsReconciled+e.openingsRehosted+e.redundantDraftsRemoved,c=e.moved+e.rotated+e.removedDrafts+e.coordsCanonicalized+e.wallsStraightened,u=Math.ceil(10*e.maxStraightenShiftCm)/10,_=(this.host._serverCfg?.spaces||[]).find(t=>String(t?.id||"")===e.maxStraightenSpace),m=(this.host._serverCfg?.spaces||[]).length>1&&_?String(_.title||_.id):"",g=e.orphanRoomLabelsRemoved+e.orphanDevicePositionsRemoved+e.orphanGroupPositionsRemoved,f=e.liveMissingPositions.map(t=>t.name).filter(Boolean),b=f.slice(0,3).join(", "),v=Math.max(0,f.length-3),y=b?this.host._t("gs.optimize_live_names",{names:b,more:v?this.host._t("gs.optimize_reference_more",{n:String(v)}):""}):"",$=e.unverifiedPositions.some(t=>"registry_unavailable"===t.reason),w=t=>e.removedPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_removed"):e.liveMissingPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_live"):this.host._t("gs.optimize_detail_unverified"),k=t=>this.host._t("room_label"===t?"gs.optimize_detail_room_label":"group"===t?"gs.optimize_detail_group":"device"===t?"gs.optimize_detail_device":"gs.optimize_detail_unknown"),x=[...e.removedPositions,...e.liveMissingPositions.filter(t=>!e.removedPositions.some(e=>e.id===t.id)),...e.unverifiedPositions],S=x.slice(0,10),M=Math.max(0,x.length-S.length);return p`{this.host._alignDialog=null,this.host._preflightClipboardFallback=null}}> +
    + ${s?p` +

    ${this.host._t("gs.align_preflight_failed",{spaces:a,more:r})}

    + ${i.slice(0,10).map(t=>p`

    + ${t.displayName}: ${this.host._t(`gs.preflight_reason_${t.reason}`)} +

    `)} + ${i.length>10?p`

    + ${this.host._t("gs.align_preflight_more",{n:String(i.length-10)})} +

    `:d} +
    ${this.host._t("gs.align_preflight_hint")}
    + ${this._preflightVersionsDiffer()?p` +
    ${this.host._t("gs.preflight_update_hint")}
    `:d} +
    + +
    + ${this.host._preflightClipboardFallback?p`
    + ${this.host._t("gs.preflight_copy")} +
    ${this.host._preflightClipboardFallback}
    +
    `:d}`:t.changed?p` + ${e.moved?p`

    ${this.host._t("gs.align_count",{n:String(e.moved),total:String(e.total),cm:String(t.cm)})}

    `:d} + ${e.latticeCoordinatesCanonicalized?p` +

    ${this.host._t("gs.optimize_lattice_summary",{n:String(e.latticeCoordinatesCanonicalized),cm:ti(e.latticeMaxShiftCm)})}

    + ${e.latticeSpaces.map(t=>p`

    ${this.host._t("gs.optimize_lattice_space",{space:t.space,n:String(t.canonicalized),far:String(t.far)})}

    `)} + `:d} + ${t.where?p`

    ${this.host._t("gs.align_where",{s:t.where})}

    `:d} + ${e.rotated?p`

    ${this.host._t("gs.align_turned",{n:String(e.rotated)})}

    `:d} + ${e.removedDrafts?p`

    ${this.host._t("gs.align_removed_drafts",{n:String(e.removedDrafts)})}

    `:d} + ${e.redundantDraftsRemoved?p`

    ${this.host._t("gs.optimize_redundant_drafts",{n:String(e.redundantDraftsRemoved)})}

    `:d} + ${e.wallSegmentsMigrated?p`

    ${this.host._t("gs.wall_segments_migrated",{n:String(e.wallSegmentsMigrated)})}

    `:d} + ${e.legacyZeroWallsMigrated?p`

    ${this.host._t("gs.zero_walls_migrated",{n:String(e.legacyZeroWallsMigrated)})}

    `:d} + ${h?p`

    ${this.host._t("gs.optimize_changes",{m:String(e.migrated),c:String(e.canonicalized),p:String(e.coordsCanonicalized),w:String(e.wallsMerged),s:String(e.spansMerged),i:String(e.partitionsMerged)})}

    `:d} + ${e.partitionsReconciled?p`

    ${this.host._t("gs.optimize_coincident_partitions",{n:String(e.partitionsReconciled)})}

    `:d} + ${e.openingsRehosted?p`

    ${this.host._t("gs.optimize_openings_rehosted",{n:String(e.openingsRehosted)})}

    `:d} + ${e.wallsStraightened?p`

    ${this.host._t("gs.optimize_walls_straightened",{n:String(e.wallsStraightened),cm:String(u)})}

    `:d} + ${m?p`

    ${this.host._t("gs.optimize_walls_straightened_where",{s:m})}

    `:d} + ${e.glowSpacesMigrated||e.glowRoomsMigrated?p`

    ${this.host._t("gs.optimize_glow_migration",{spaces:String(e.glowSpacesMigrated),rooms:String(e.glowRoomsMigrated)})}

    `:d} + ${c?p`
    ${this.host._t("gs.align_warn")}
    `:d}`:p`

    ${this.host._t(e.liveMissingPositions.length||e.unverifiedPositions.length||e.nestedRefsUnresolved?"gs.optimize_no_automatic_changes":"gs.align_none")}

    `} + ${!s&&e.wallsStraightenSkipped?p`

    ${this.host._t("gs.optimize_walls_straighten_skipped",{n:String(e.wallsStraightenSkipped)})}

    `:d} + ${l?p`

    ${this.host._t("gs.optimize_references",{spaces:String(e.spaceRefsRemapped),rooms:String(e.roomRefsRemapped),positions:String(e.positionsRemapped),detached:String(e.markersDetached)})}

    `:d} + ${g?p`

    ${this.host._t("gs.optimize_orphans_removed",{total:String(g),rooms:String(e.orphanRoomLabelsRemoved),devices:String(e.orphanDevicePositionsRemoved),groups:String(e.orphanGroupPositionsRemoved)})}

    `:d} + ${e.liveMissingPositions.length?p`
    +

    ${this.host._t(t.removeLiveMissingPositions?"gs.optimize_live_positions_remove":"gs.optimize_live_positions",{n:String(e.liveMissingPositions.length),names:y})}

    + + ${t.removeLiveMissingPositions?p`
    + ${this.host._t("gs.optimize_live_selected")} +
    `:d} +
    `:d} + ${e.unverifiedPositions.length?p``:d} + ${e.nestedRefsUnresolved?p``:d} + ${x.length?p`
    + ${this.host._t("gs.optimize_details")} +
      + ${S.map(t=>p`
    • ${this.host._t("gs.optimize_detail_item",{status:w(t),kind:k(t.kind),id:t.id,space:t.spaceId})}
    • `)} +
    + ${M?p`
    ${this.host._t("gs.optimize_details_more",{n:String(M)})}
    `:d} +
    `:d} +
    +
    + + + ${t.changed&&t.preflight?.ok?p` + `:d} +
    +
    `}_renderSettingsDialog(){return p`this.host._settingsDialog=null}> +
    +
    ${ko(l(this.host.hass,this.host._config?.language),"gs.hint")}
    + this.host._settingsDialog={...this.host._settingsDialog,zigbeeTopology:t.detail}}> + + ${this._renderColorRow("light_on","gs.light_on")} + ${this._renderColorRow("light_off","gs.light_off")} + ${this._renderColorRow("light_none","gs.light_none")} + + ${this._renderColorRow("temp_cold","gs.temp_cold")} + ${this._renderColorRow("temp_ok","gs.temp_ok")} + ${this._renderColorRow("temp_hot","gs.temp_hot")} + + ${this._renderColorRow("lqi_low","gs.lqi_low")} + ${this._renderColorRow("lqi_high","gs.lqi_high")} + + ${this._renderColorRow("glow_base","gs.glow_base")} + ${this._renderColorRow("glow_light","gs.glow_light")} +
    + + ${this._help("gs.glow_radius.help")} + {const e=Oh(t.target.value);null!=e&&e>0&&(this.host._settingsDialog={...this.host._settingsDialog,glowRadius:e})}} /> + ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} +
    + + ${this._renderColorRow("wall_fill","gs.wall_fill")} + +
    + + ${this._help("gs.bg_mode.help")} + +
    + ${"static"===this.host._settingsDialog.bgMode?p`
    + {this.host._settingsDialog={...this.host._settingsDialog,bgColor:t.detail.color}}}> + ${this.host._settingsDialog.bgColor?p``:p`${this.host._t("gs.bg_theme")}`} +
    `:d} + + ${ei(this.host.hass)?d:p`
    ${this.host._t("gs.sun_missing")}
    `} +
    + ${this.host._renderCompass()} +
    +
    + + ${this._help("gs.north.help")} +
    +
    + {const e=t.target.value.trim(),s=""===e?null:Math.round(Number(e));this.host._settingsDialog={...this.host._settingsDialog,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} /> + ${null!==this.host._settingsDialog.northDeg?p``:d} +
    +
    +
    + + ${this.host._canEdit?p` + +
    ${this.host._t("gs.backup_hint")}
    +
    + + + + this._pickBackupImport(t)} /> + + ${this.host._canOptimizeUndo&&"import"===this.host._undoKind?p` + `:d} +
    `:d} + +
    ${this.host._t("gs.grid_hint")}
    +
    + +
    + ${this.host._canOptimizeUndo&&"import"!==this.host._undoKind?p`
    + +
    `:d} +
    +
    + + + + +
    +
    `}_rulesSet(t){this.host._rulesDialog={...this.host._rulesDialog,rules:t}}async _saveRules(){const t=this.host._rulesDialog;if(!t||t.busy)return;const e=t.rules.filter(t=>t.pattern.trim()&&t.icon.trim());this.host._rulesDialog={...t,busy:!0};try{const t=this.host._serverCfg,s=JSON.stringify(e)===JSON.stringify(le),i={...t.settings};s?delete i.icon_rules:i.icon_rules=e,this.host._serverCfg={...t,settings:i},await this._saveConfigNow(),this.host._rulesDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("rules.saved"))}catch(t){this.host._rulesDialog&&(this.host._rulesDialog={...this.host._rulesDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_renderRulesDialog(){const t=this.host._rulesDialog,e=si(t.rules),s=t.test.trim()?ii(t.test,"",e):null,i=(e,s)=>{const i=[...t.rules],o=e+s;o<0||o>=i.length||([i[e],i[o]]=[i[o],i[e]],this._rulesSet(i))};return p`this.host._rulesDialog=null}> +
    +
    ${this.host._t("rules.hint")}
    +
    + this.host._rulesDialog={...t,test:e.target.value}} /> + ${s?p`${s}`:d} +
    + ${t.rules.map((e,s)=>{const o=""!==e.pattern.trim()&&!oi(e.pattern);return p`
    + {const o=[...t.rules];o[s]={...e,pattern:i.target.value},this._rulesSet(o)}} /> + {const o=[...t.rules];o[s]={...e,icon:i.target.value},this._rulesSet(o)}} /> + + i(s,-1)}> + i(s,1)}> + this._rulesSet(t.rules.filter((t,e)=>e!==s))}> +
    `})} + +
    +
    + + + + +
    +
    `}_saveKioskScale(t){this.host._kioskScale={...this.host._kioskScale,...t};try{localStorage.setItem("houseplan_card_kiosk_v1",JSON.stringify(this.host._kioskScale))}catch{}this.host.requestUpdate()}_renderKioskDialog(){const t=this.host._kioskScale,e=(e,s)=>p` +
    + ${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))} + ${Math.round(100*t[e])}% +
    `;return p`this.host._kioskDialog=!1}> +
    +
    ${this.host._t("kiosk.hint")}
    + ${e("icon",this.host._t("kiosk.icon_scale"))} + ${e("font",this.host._t("kiosk.font_scale"))} +
    +
    + + + +
    +
    `}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return d;const s=e.marker?.vacuum||{},o=this.host._vacAllCamerasFor===e.id,a=this.host._vacSourceResolution(e,o),n=a.entityId,r=a.candidates.find(t=>t.entityId===n)||null,l=n?ai(this.host.hass?.states[n]?.attributes):null,h=!1!==s.live&&("ok"===a.status||"unsupported"===a.status),c=this._vacPlanRoomAnchors(e.space),u=l?ni(l.rooms,c.map(t=>t.name)):0,_=!!(h&&l&&u>=3),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=a.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=o?a.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>p` + `,$=a.candidates.some(t=>g.has(t.entityId)&&"known_xcme_incomplete"===t.category)||!!a.pinned&&!!n?.startsWith("camera.")&&!r?.hasPosition;return p` + +
    +
    +
    ${this.host._t("vac.diag_source")}${n||this.host._t("vac.source_none")}
    + ${r?.platform?p`
    ${this.host._t("vac.diag_platform")}${r.platform}
    `:d} +
    ${this.host._t("vac.diag_status")}${this.host._t(`vac.source_status_${a.status}`)}
    +
    ${this.host._t("vac.diag_position")}${l?.pos?this.host._t("common.yes"):this.host._t("common.no")}
    +
    ${this.host._t("vac.diag_rooms")}${i(this.host._t("vac.diag_rooms_value"),{total:String(l?.rooms.length||0),matched:String(u),readiness:this.host._t(u>=3?"vac.autocal_ready":"vac.autocal_not_ready")})}
    +
    ${this.host._t("vac.diag_path")}${l?.path.length?this.host._t("common.yes"):this.host._t("common.no")}
    +
    ${this.host._t("vac.diag_map")}${l?.mapId??"default"}
    +
    + ${"missing"===a.status||"disabled"===a.status||"unverified"===a.status?p`
    + ${this.host._t(`vac.source_banner_${a.status}`)} + +
    `:d} + ${$?p`
    + ${this.host._t("vac.xcme_hint")} +
    attributes:
    +  - vacuum_position
    +  - rooms
    +  - path
    +  - map_name
    +
    `:d} +
    + ${this.host._t("vac.choose_source")} +
    + + ${f.map(y)} +
    {t.currentTarget.open?this.host._vacOpenAllCameras(e):(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null)}}> + ${this.host._t("vac.all_cameras")} +
    ${this.host._t("vac.all_cameras_warn")}
    + ${o?b.length?b.map(y):p`
    ${this.host._t("vac.all_cameras_empty")}
    `:d} +
    +
    +
    +
    + ${_?p``:d} + ${h?p``:d} + ${this.host._t("vac.documentation")} +
    + ${l?p` + + + + ${Io(this,e,a)} + `:d} +
    `}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i,o=""){return hh(this,t,e,s,i,o)}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:at(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=li(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}async _vacAutoCalibrate(t){if(this.host._markerDialog?.busy)return;const e=this.host._vacSource(t),s=e?ai(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacMapId(t,s),a=function(t,e,s,i,a){const n=o(t,e,s,i).find(t=>t.source===i&&t.map_id===a);return{space:n?.space||s,routeId:n?.id||""}}(t.id,t.marker?.vacuum??null,t.space,e,i),n=hi(s.rooms,this._vacPlanRoomAnchors(a.space));if(!n)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const r=this.host._serverCfg?.spaces?.find(t=>t.id===a.space),l=Number(r?.cell_cm),h=Number.isFinite(l)&&l>0?l:5,c=ci(n.residual,this.host._gridPitch,h);c>di?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length,error:Te(c,"mi"===this.host.hass?.config?.unit_system?.length)}:await async function(t,e){if(rh.has(t))return;rh.add(t);const s=t.host,i=s._markerDialog,o=i?{...i,busy:!0}:null;o&&(s._markerDialog=o,s.requestUpdate());try{await hh(t,e.markerId,e.source,e.mapId,e.matrix,e.routeId)&&s._showToast(s._t("vac.autocal_done",{rooms:String(e.rooms)}))}finally{rh.delete(t),o&&s._markerDialog===o&&(s._markerDialog={...o,busy:!1},s.requestUpdate())}}(this,{markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length})}_vacApplyCalibrationProposal(t){return async function(t,e){const s=t.host,i=s._vacCalConfirm;if(!i||i.busy)return;if(e){const t=s._devices.find(t=>t.id===i.markerId),e=n(i.matrix);if(!t||!e)return;const o=i.space||t.space;if(o!==s._space&&!s._commitSpace(o))return;return s._vacCalConfirm=null,s._markerDialog=null,void(s._vacFit={markerId:i.markerId,source:i.source,routeId:i.routeId,mapId:i.mapId,p:e,drag:null})}const o={...i,busy:!0};s._vacCalConfirm=o,s.requestUpdate();const a=await hh(t,i.markerId,i.source,i.mapId,i.matrix,i.routeId);s._vacCalConfirm===o&&(s._vacCalConfirm=a?null:{...i,busy:!1},s.requestUpdate()),a&&s._showToast(s._t("vac.autocal_done",{rooms:String(i.rooms)}))}(this,t)}_vacStartFit(t,e=""){if(this.host._markerDialog?.busy)return;const s=this.host._vacSource(t),i=s?ai(this.host.hass?.states[s]?.attributes):null;if(!s||!i)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const l=this._vacMapId(t,i),h=function(t,e,s){const i=o(t,e,s.dockSpace,s.source),l=i.find(t=>t.id===s.routeId)||i.find(t=>t.source===s.source&&t.map_id===s.mapId)||null,h=l?.space||s.dockSpace,c=s.viewBoxOf(h);if(!c)return null;const d=a(l?l.calibration:e?.calibration?.[s.mapId]),p=d&&n(d)||r(s.rooms,c);return{space:h,routeId:l?.id||"",params:p}}(t.id,t.marker?.vacuum??null,{routeId:e,source:s,mapId:l,dockSpace:t.space,rooms:i.rooms,viewBoxOf:t=>this.host._spaceModelById(t)?.vb??null});h&&(this.host._markerDialog=null,(h.space===this.host._space||this.host._commitSpace(h.space))&&(this.host._vacFit={markerId:t.id,source:s,mapId:l,routeId:h.routeId,p:h.params,drag:null}))}_vacFitSave(){return async function(t,e){const s=t.host,i=s._vacFit;if(!i||i.busy)return;const o={...i,busy:!0,drag:null};s._vacFit=o,s.requestUpdate();const a=await hh(t,i.markerId,i.source,i.mapId,e(i.p),i.routeId);s._vacFit===o&&(s._vacFit=a?null:{...i,busy:!1,drag:null},s.requestUpdate()),a&&s._showToast(s._t("vac.cal_done"))}(this,ui)}_vacFitTurn(t){const e=this.host._vacFit;if(!e||e.busy)return;const s=ai(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:pi(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),s.busy)return;if("pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=ai(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=ui(i.p0),[r,l]=_i(n,e[0],e[1]),[h,c]=_i(n,i.fx,i.fy),d=Math.hypot(r-h,l-c)||1,[p,u]=[2*r-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*a-c)/2,m=Math.max(.05,_/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:pi(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return d;const s=ai(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return d;const i=ui(e.p),o=[],a=[],n=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>_i(i,t,e));e.forEach(([t,e])=>{a.push(t),n.push(e)});const[s,r]=_i(i,t.cx,t.cy);o.push(f` + ${t.name}`)}let r=d;if(s.pos){const[e,o]=_i(i,s.pos.x,s.pos.y);r=f``}const l=[];if(a.length){const e=(()=>{const t=i[0]*i[4]-i[1]*i[3];return(e,s)=>[(i[4]*(e-i[2])-i[1]*(s-i[5]))/t,(-i[3]*(e-i[2])+i[0]*(s-i[5]))/t]})(),s=Math.min(...a),o=Math.max(...a),r=Math.min(...n),h=Math.max(...n),c=.022*t.w,d=c/4;for(const[t,i,a,n]of[[s,r,o,h],[o,r,s,h],[o,h,s,r],[s,h,o,r]]){const s=e(a,n);l.push(f` + `)}}return p`this._vacFitPointer(e,t)} + @pointermove=${e=>this._vacFitPointer(e,t)} + @pointerup=${e=>this._vacFitPointer(e,t)} + @pointercancel=${e=>this._vacFitPointer(e,t)}>${o}${r}${l}`}_resetRoomDialogFields(){this.host._roomEditId=null,this.host._roomFill="",this.host._roomCustomFill=null,this.host._roomTempSrc="",this.host._roomHumSrc="",this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomNameScale=1,this.host._roomLabelScale=1}_openRoomEdit(t){if(!t.id)return;this.host._roomEditId=t.id,this.host._nameSel=t.name||"",this.host._areaSel=t.area||"",this.host._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this.host._roomCustomFill=e&&"object"==typeof e?Os(e,Es(this.host._curSpaceCfg).customFill):null,this.host._roomTempSrc=t.settings?.temp_source||"",this.host._roomHumSrc=t.settings?.hum_source||"",this.host._roomNameScale=mi(t.settings?.name_scale),this.host._roomLabelScale=mi(t.settings?.label_scale),this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this.host._roomFill&&(t.fill_mode=this.host._roomFill),this.host._roomCustomFill&&(t.custom_fill=this.host._roomCustomFill),this.host._roomTempSrc&&(t.temp_source=this.host._roomTempSrc),this.host._roomHumSrc&&(t.hum_source=this.host._roomHumSrc),1!==this.host._roomNameScale&&(t.name_scale=this.host._roomNameScale),1!==this.host._roomLabelScale&&(t.label_scale=this.host._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this.host._curSpaceCfg,e=t?.rooms.find(t=>t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=Z(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(Q(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Wh,y:s.y*Wh};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},Gh(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),l=this.host._viewOr(n),h=(t.clientX-this.host._drag.sx)/r.width*l.w,c=(t.clientY-this.host._drag.sy)/r.height*l.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=pe(this.host._drag.ox+h),p=pe(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},Gh(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/Wh,y:a.y/Wh,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return d;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=He(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return d;const o=(i[0]-s.x)/s.w*100,a=(i[1]-s.y)/s.h*100;return p``}_alignCandidates(){const t=[],e=this.host._spaceModel();if(this.host._markup){if(!e)return t;if(this.host._drag?.id.startsWith("rl_")){const s=this.host._drag.id.slice(3);for(const i of e.rooms){if(!i.name||i.id===s)continue;const e=this._labelPos(i,this.host._space);t.push([e.x,e.y])}return t}for(const s of e.rooms){const e=at(s);if(e)for(const s of e)t.push(s)}if("draw"===this.host._tool)for(const e of this.host._path)t.push(e);if("split"===this.host._tool&&this.host._splitSel?.pts)for(const e of this.host._splitSel.pts)t.push(e);return t}if("devices"===this.host._mode){const e=this.host._deviceDrag?.id??this.host._drag?.id;for(const s of this.host._devices){if(s.space!==this.host._space||s.id===e||"ha_disabled"===s.bindingStatus?.kind)continue;const i=this.host._pos(s);t.push([i.x,i.y])}return t}if("decor"===this.host._mode){const e=this.host._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this.host._decorDraft&&t.push(this.host._decorDraft.a),t}return t}_renderAlignGuides(){const t=this.host._alignPoint;if(!t)return f``;const e=this.host._drag?.id.startsWith("rl_")?.5*this.host._gridPitch:.05*this.host._gridPitch,s=gi(t,this._alignCandidates(),e);if(!s.length)return f``;const i=this.host._gridPitch,o=1.5*i;return f` + ${s.map(e=>{const[s,a,n,r]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return f` + `})} + `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180,s=k(15,this.host._cellCm);return f``}_renderOpeningDimensionGuides(t){const e=t.labels.flatMap(t=>t.dimension?[t.dimension]:[]);if(!e.length)return f``;const s=this._cssPxToRender(4);return f``}_renderOpeningPlacementPreview(){const t=this.host._openingPreview;if(!t)return f``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:fi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:t.face},s="passage"===t.type?bi(t,k(this.host._gridPitch,this.host._cellCm)):null;return f` + `}_renderOpeningDialog(){const t=this.host._openingDialog,e=this.host._spaceModel()?.partitions||[],s=t.id?this.host._curSpaceCfg?.openings?.find(e=>e.id===t.id):null,i={id:t.id||"preview",type:t.type,x:t.x/Wh,y:t.y/this.host._spaceH,angle:t.angle,length:s&&!t.lengthTouched?s.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/Wh,...t.host?{host:t.host}:{}},o=fs(s,i),a=t.host?o?_t(i,e,Wh,this.host._cellCm,this.host._gridPitch):nt(i,e,Wh,this.host._cellCm,this.host._gridPitch):null,n="does-not-fit-jamb"===a?.reason,r=t.host?e.find(e=>e.id===t.host.id):null,l=r?Te(r.cm/2,this.host._imperial):"",h=!!t.host&&!a?.resolved&&!n,c="gate"===t.type?"mdi:gate":"window"===t.type?"mdi:window-closed-variant":"passage"===t.type?"mdi:arch":"mdi:door",u=(e,s)=>{const i="contact"===e?t.contact:t.lock,o="contact"===e?!!t.contactOpen:!!t.lockOpen,a="contact"===e?t.contactFilter||"":t.lockFilter||"",n=s.find(t=>t.value===i),r=n?.label||this.host.hass.states[i]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[i]?.name||i,l=Qi(s,a);return p` + + ${o?p`
    + this._filterOpeningEntities(e,t.target.value)} /> +
    + + ${l.map(t=>p` + `)} + ${l.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} +
    +
    `:d}`};return p`this.host._openingDialog=null}> +
    + ${t.host?p` +
    + ${h||n?p``:d} + ${h?this.host._t("opening.partition_orphan"):n?this.host._t("opening.partition_jamb_margin",{distance:l}):this.host._t("opening.host_partition")} +
    `:d} + + + + + + + + {const s=Oh(e.target.value);null!=s&&(this.host._openingDialog={...t,lengthCm:s,lengthTouched:!0})}} /> + + ${"passage"===t.type&&(t.contact||t.lock)?p`
    + + ${this.host._t("opening.passage_binding_warning")} +
    `:d} + + ${"passage"!==t.type?p` + ${u("contact",this._contactCandidates())} + ${t.contact?p``:d}`:d} + + ${"door"===t.type||"gate"===t.type?p` + ${u("lock",this._lockCandidates())}`:d} + + ${"gate"!==t.type&&"passage"!==t.type?p``:d} + ${"passage"!==t.type?p``:d} +
    + +
    `}_gridLevels(){const t=this.host._stageEl,e=this.host._viewOr(this.host._baseVb()),s=t&&t.clientWidth&&e.w?t.clientWidth/e.w:1;return $i(this.host._gridPitch,s)}_renderMarkupDefs(t){const e=this._gridLevels();if(!e)return f``;const s=this.host._gridPitch*e.fine,i=this.host._gridPitch*e.coarse,o=this.host._gridPitch*e.fine*.14;return f` + + + + + + + + + + + + + + `}_renderPhysicalEditorLayer(){const t=this.host._spaceModel();if(!t)return f``;const e=this.host._gridPitch,s=this.host._viewOr(this.host._baseVb()),i=this.host._stageEl,o=i?.clientWidth?s.w/i.clientWidth:e/8,a=Math.max(.22*e,8*o),n=Math.max(a,12*o),r=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`,l=(t,e)=>this.host._physicalSel?.kind===t&&this.host._physicalSel.id===e||"column"===t&&this.host._duplicateColumnId===e,h=(t.room_drafts||[]).flatMap(t=>t.points.slice(0,-1).map((e,s)=>{const i=t.points[s+1],a=Number(t.segments[s]?.cm),n=x(Number.isFinite(a)?a:15,this.host._cellCm,this.host._gridPitch);return f`{this.host._physicalSel={kind:"draft",id:t.id,segment:s},this._registerPhysicalTap("draft",t.id,s)}}>`})),c=(t.partitions||[]).map(t=>{const e=De(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);return e?f`this._physicalDown(e,"partition",t.id)} + @pointermove=${t=>this._physicalMove(t)} + @pointerup=${t=>this._physicalUp(t)}>`:f`this._physicalDown(e,"partition",t.id)} + @pointermove=${t=>this._physicalMove(t)} + @pointerup=${t=>this._physicalUp(t)}>`}),p=(t.wall_columns||[]).map(t=>{const e=this.host._physicalRotate?.id===t.id?{...t,angle:this.host._physicalRotate.angle}:t,s=Me(e,this.host._cellCm,this.host._gridPitch);return f`this._physicalDown(e,"column",t.id)} + @pointermove=${t=>this._physicalMove(t)} + @pointerup=${t=>this._physicalUp(t)}>`}),u=this.host._physicalDrag,_=(()=>{if(!u?.moved)return d;if("partition"===u.kind&&0===Number(u.base.cm)){const t=u.base;return f``}const t="partition"===u.kind?De(u.base.a,u.base.b,u.base.cm,this.host._cellCm,this.host._gridPitch):Me(u.base,this.host._cellCm,this.host._gridPitch);return t?f``:d})(),m=(()=>{const s=this.host._physicalSel;if(!s)return d;if("draft"===s.kind){const e=t.room_drafts.find(t=>t.id===s.id);return e?f` + t.join(",")).join(" ")}> + ${e.points.map(t=>f``)} + `:d}if("partition"===s.kind){const e=t.partitions.find(t=>t.id===s.id);if(!e)return d;const i=De(e.a,e.b,e.cm,this.host._cellCm,this.host._gridPitch),o=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2;return f` + ${i?f``:f``} + + + + `}const i=t.wall_columns.find(t=>t.id===s.id);if(!i)return d;const l=this.host._physicalRotate?.id===i.id?{...i,angle:this.host._physicalRotate.angle}:i,h=Me(l,this.host._cellCm,this.host._gridPitch);if("square"!==l.shape)return f` + + + `;const c=x(l.cm,this.host._cellCm,this.host._gridPitch),p=ge(l.angle)*Math.PI/180,u=Math.sin(p),_=-Math.cos(p),m=[l.center[0]+u*c/2,l.center[1]+_*c/2],g=[m[0]+u*Math.max(e,24*o),m[1]+_*Math.max(e,24*o)];return f` + + + + this._physicalRotateDown(t,i)} + @pointermove=${t=>this._physicalRotateMove(t)} + @pointerup=${t=>this._physicalRotateUp(t)}> + `})();return f`${h}${c}${p}${_}${m}`}_renderHiddenWallDiagnosticOverlay(){if(!this.host._markup)return f``;const t=this._hiddenWallDiagnosticSnapshot().value;if(!t.segments.length)return f``;const e=x(5,this.host._cellCm,this.host._gridPitch);return f``}_renderPlanSnapOverlay(){if(!this.host._markup)return f``;const t=this._planSnapGeometrySnapshot().value,e=this.host._activePlanSnapCandidate,s=new Set(this.host._activePlanSnapConflicts.map(t=>t.key)),i=x(5,this.host._cellCm,this.host._gridPitch),o=x(10,this.host._cellCm,this.host._gridPitch);return f``}_syncPlanSnapActiveMarker(t){const e=this.host.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(e){if(e.setAttribute("class",`plan-snap-node${t?" active":""}${"line"===t?.kind?" dynamic":""}`),e.setAttribute("data-active",t?"true":"false"),e.setAttribute("visibility",t?"visible":"hidden"),!t)return e.removeAttribute("data-kind"),void e.removeAttribute("data-key");e.setAttribute("data-kind",t.kind),e.setAttribute("data-key",t.key),e.setAttribute("cx",String(t.point[0])),e.setAttribute("cy",String(t.point[1])),e.setAttribute("r",String(x(10,this.host._cellCm,this.host._gridPitch)))}}_syncPlanSnapConflictMarkers(t){const e=new Set(t.map(t=>t.key));for(const t of this.host.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])t.setAttribute("class","plan-snap-node"+(e.has(t.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(t){let e=0;const s=this.host._spaceModel();if(!s)return null;if("partition"===t.sourceKind)e=Number(s.partitions.find(e=>e.id===t.sourceId)?.cm)||0;else if("draft"===t.sourceKind){const i=/^(.*):(\d+)$/.exec(t.sourceId),o=i?s.room_drafts.find(t=>t.id===i[1]):null;e=o&&i&&Number(o.segments[Number(i[2])]?.cm)||0}else e=wi(s.rooms,this.host._spaceWalls,this.host._openCuts(),[t.a[0],t.a[1],t.b[0],t.b[1]],this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh);return e>0?{a:[...t.a],b:[...t.b],halfDepth:x(e,this.host._cellCm,this.host._gridPitch)/2}:null}_drawPreviewJoinPatchD(t,e){if(t.length<2)return"";const s=[];for(let i=0;i+10&&s.push({a:t[i],b:t[i+1],halfDepth:e[i]});if(!s.length)return"";const i=2e-4*this.host._gridPitch,o=this._planSnapGeometrySnapshot().value.segments.filter(e=>t.some(t=>st(t,[e.a[0],e.a[1],e.b[0],e.b[1]])<=i)).map(t=>this._planSnapPhysicalSegment(t)).filter(t=>!!t);return ki([...s,...o],i).map(t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(t){const e=this.host._openCuts(),s=this.host._thickWallCuts(),i=e.concat(s),o=i.length?et(this.host._segments,i,.02*this.host._gridPitch):this.host._segments,a=this.host._path,n=this.host._gridPitch,r=this.host._viewOr(this.host._baseVb()),l="draw"===this.host._tool?this.host._drawWallCm:null,h=(()=>"draw"===this.host._tool&&a.length&&null!=l?this.host._contourClosed?a:this.host._cursorPt?[...a,this.host._cursorPt]:a.length>=2?a:null:null)(),c=h?or(h.length-1,this.host._contourClosed?[...this.host._draftSegmentCms,this.host._closingWallCm??void 0]:this.host._draftSegmentCms,l,ht).map(t=>x(t,this.host._cellCm,this.host._gridPitch)/2):[],p=h?xi(h,x(l,this.host._cellCm,this.host._gridPitch)/2,this.host._contourClosed,c):"",u=h?this._drawPreviewJoinPatchD(h,c):"",_=this.host._wallFaceBatch?.candidates[this.host._wallFaceBatch.index]?.repair||this.host._wallRepairDiagnostic;return f` + ${this._gridLevels()?f``:d} + ${o.map(t=>f``)} + ${this._renderPhysicalEditorLayer()} + ${"column"===this.host._tool&&this.host._cursorPt&&this.host._drawWallCm?f`{const t={shape:"square",center:this.host._cursorPt,cm:this.host._drawWallCm};return`M ${Me(t,this.host._cellCm,this.host._gridPitch).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`})()}>`:d} + ${p?f` + `:d} + ${h&&0===l?f`t.join(",")).join(" ")}>`:d} + ${u?f` + `:d} + ${_?f``:d} + ${"split"===this.host._tool&&this.host._splitSel?.pts?.length?f`${this.host._splitSel.pts.length>1?f``:d} + ${this.host._splitSel.pts.map((t,e)=>f``)} + ${this.host._cursorPt?f``:d}`:d} + `}_renderActiveChainInk(){const t=this.host._path,e=this.host._gridPitch;return f` + ${t.length>1?f``:d} + ${t.length&&this.host._cursorPt&&"draw"===this.host._tool&&!this.host._contourClosed?f` + ${this.host._activePlanSnapCandidate||this.host._activePlanSnapConflicts.length?d:f``}`:d} + ${t.map((t,s)=>f``)} + `}_renderPartitionDeleteDialog(){const t=this.host._partitionDeleteDialog,e="mi"===this.host.hass?.config?.unit_system?.length;return p`this.host._partitionDeleteDialog=null}> +
    +

    ${this.host._t("confirm.delete_partition_openings_body",{count:t.openings.length})}

    +
      + ${t.openings.map(t=>p`
    • ${this.host._t("confirm.delete_partition_openings_item",{type:this.host._t(`opening.${t.type}`),length:Te(t.length*Wh/this.host._gridPitch*this.host._cellCm,e)})}
    • `)} +
    +
    +
    + + + +
    +
    `}_renderRoomDeleteDialog(){const t=this.host._roomDeleteDialog;return p`this.host._roomDeleteDialog=null}> +
    +

    ${this.host._t("confirm.delete_room_body")}

    +
    +
    + + + + +
    +
    `}_renderPhysicalDialog(){const t=this.host._physicalDialog,e="column"===t.kind;return p`this.host._physicalDialog=null}> +
    + ${e?p` + `:d} + +
    this.host._physicalDialog={...t,cm:e.target.value}} /> + ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    + ${e&&"square"===t.shape?p` + + this.host._physicalDialog={...t,angle:e.target.value}} />`:d} + ${t.length?p`
    ${this.host._t("physical.length")}: ${t.length}
    `:d} +
    + +
    `}_renderMarkupBar(){const t=this.host._geometryHistory.undoName,e=this.host._geometryHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    +
    + + + + + + + + + + ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} + + + + +
    +
    + +
    +
    `}_renderDevicesBar(){const t=this.host._devicePositionHistory.undoName,e=this.host._devicePositionHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    +
    + + + + + ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} +
    +
    + + + +
    +
    `}_discoveryFilterState(t){const e=this.host._settings,s=void 0!==t.draftGroupLights?t.draftGroupLights:!1!==e.group_lights,i=e.exclude_integrations,o=void 0!==t.draftExcluded?null===t.draftExcluded?[...Si]:t.draftExcluded:i?[...i]:[...Si],a=void 0!==t.draftExcluded?null===t.draftExcluded:!i,n=void 0!==t.draftGroupLights||void 0!==t.draftExcluded;return{groupLights:s,excluded:o.sort(),usesProductList:a,dirty:n}}_registryIntegrations(){const t=new Set,e=this.host._fullRegistryHass;for(const s of Object.values(e?.entities||{}))s?.platform&&t.add(String(s.platform));for(const s of Object.values(e?.devices||{})){const e=s?.identifiers?.[0],i=Array.isArray(e)?e[0]:null;i&&t.add(String(i))}return[...t].sort()}_discoveryFilterPreview(t){const e=this._discoveryFilterState(t),s=[e.groupLights?1:0,e.usesProductList?"p":e.excluded.join(","),this.host._cfgEpoch,this.host._regSignature].join("|");if(this._discoveryPreviewMemo?.key===s)return this._discoveryPreviewMemo.value;const i={hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),markers:this.host._markers,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||"",loc:t=>this.host._t(t),iconRules:this.host._iconRules},o=(t,e)=>new Set(to({...i,settings:t,excluded:e})),a=(t,e)=>new Set(eo({...i,settings:t,excluded:e}).map(t=>t.bindingRef||t.id)),n=Mi(this.host._settings),r={...this.host._settings,group_lights:e.groupLights},l=new Set(e.excluded),h=o(this.host._settings,n),c=o(r,l),d=a(this.host._settings,n),p=a(r,l);let u=0,_=0,m=0;const g=t=>t.includes("light.");for(const t of h)c.has(t)||(u++,g(t)&&m++);for(const t of c)h.has(t)||(_++,g(t)&&m++);for(const t of p)d.has(t)||(u++,g(t)&&m++);for(const t of d)p.has(t)||(_++,g(t)&&m++);const f={appear:u,hide:_,lights:m};return this._discoveryPreviewMemo={key:s,value:f},f}async _saveDiscoveryFilters(t){const e=this.host._serverCfg;if(!e)return;const s=this._discoveryFilterState(t),i={...e.settings};s.groupLights?delete i.group_lights:i.group_lights=!1,s.usesProductList?delete i.exclude_integrations:i.exclude_integrations=s.excluded,this.host._serverCfg={...e,settings:i},this.host._deviceInboxMemo=null,this._discoveryPreviewMemo=null,this.host._regSignature="",this.host._deviceInbox&&(this.host._deviceInbox={...t,draftGroupLights:void 0,draftExcluded:void 0}),this._saveConfig(),this.host._maybeRebuildDevices(),this.host.requestUpdate()}_renderDiscoveryFilters(t){const e=this._discoveryFilterState(t),s=e=>this.host._deviceInbox={...t,...e},i=t.filtersOpen&&e.dirty?this._discoveryFilterPreview(t):null,o=this._registryIntegrations().filter(t=>!e.excluded.includes(t));return p`
    s({filtersOpen:t.target.open})}> + ${this.host._t("device_inbox.filters_title")} + +
    + ${this.host._t("device_inbox.filters_excluded")} +
    + ${e.excluded.map(t=>p`${t} + + `)} +
    + {const i=t.target,o=i.value.trim();o&&!e.excluded.includes(o)&&s({draftExcluded:[...e.excluded,o]}),i.value=""}} /> + + ${o.map(t=>p``)} + + +
    + ${i?p`
    + ${this.host._t("device_inbox.filters_preview_appear",{count:String(i.appear)})} + · ${this.host._t("device_inbox.filters_preview_hide",{count:String(i.hide)})} + · ${this.host._t("device_inbox.filters_preview_lights",{count:String(i.lights)})} +
    `:d} + +
    `}_renderDeviceInbox(){const t=this.host._deviceInbox,e=this._deviceInboxRows(),s=Object.fromEntries(["on_plan","available","hidden","readd"].map(t=>[t,e.filter(e=>e.category===t).length])),i=function(t,e,s,i=!1){const o=s.trim().toLocaleLowerCase();return t.filter(t=>t.category===e&&(!i||t.isNew)&&(!o||t.searchText.includes(o)))}(e,t.tab,t.search,"on_plan"===t.tab&&t.onlyNew),o=i.slice(0,t.limit),a=t=>this.host._t(`device_inbox.tab_${t}`),n=`device_inbox.empty_${t.tab}`;return p`this.host._deviceInbox=null}> +
    +
    + this.host._deviceInbox={...t,search:e.target.value,limit:100}} /> + +
    +
    this._deviceInboxTabKey(t)}> + ${["on_plan","available","hidden","readd"].map(e=>p` + `)} +
    +
    + ${"on_plan"===t.tab?p``:d} + ${"available"===t.tab?p``:d} + + + ${this._help("device_inbox.show_hidden.help")} + +
    + ${"available"===t.tab?this._renderDiscoveryFilters(t):d} +
    + ${o.length?o.map(t=>{const e="on_plan"===t.category?t.canFind?p``:p``:"hidden"===t.category?p``:p``,s="active"===t.status.kind?"":this.host._t(`device_inbox.status_${t.status.kind}`);return p`
    + +
    +
    ${t.name} + ${t.isNew?p`${this.host._t("device_inbox.new")}`:d} +
    +
    + ${[t.model,t.integration,t.spaceName,t.areaName].filter(Boolean).join(" · ")} +
    +
    + ${"excluded_integration"===t.reason&&t.integration?this.host._t("device_inbox.reason_excluded_integration",{integration:t.integration}):this.host._t(`device_inbox.reason_${t.reason}`)} + ${s?p`${s}`:d} +
    + ${t.binding} +
    +
    + ${e} + ${t.canEdit||t.canHide||"available"===t.category||"hidden"===t.category||this.host._bindingHasHaPage(t.binding)?p` +
    + + + +
    + ${t.canEdit&&("on_plan"!==t.category||t.canFind)?p``:d} + ${t.canHide?p``:d} + ${"available"===t.category?p``:d} + ${"hidden"===t.category?p``:d} + ${this.host._bindingHasHaPage(t.binding)?p``:d} +
    +
    `:d} +
    +
    `}):p`
    ${this.host._t(n)}
    `} +
    + ${i.length>o.length?p``:d} +
    +
    + +
    +
    `}_markerValueBadgeFields(t){return Di({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerValueSourceFields(t){return Ci({touched:t.valueSourceTouched,originalHas:t.originalHasValueSource,original:t.originalValueSource,source:t.valueSource})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=zs(t.room),s=xs(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=Cs(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Oh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null},r=!!i&&("string"==typeof i.area&&i.area.length>0||null===i.area&&!!i.space&&!!i.room_id);return"virtual"===t.binding||t.roomTouched?(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null):r&&(n.space=i.space,n.area=i.area,n.room_id=i.room_id),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${_(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=Pi({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return Ii(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:Ps(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=Ms(this.host._planHass,i,o),n=Ds(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return Ti(zi(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&Ri(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&Ai(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of Ai(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${Ai(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>Fi(e)&&!i.has(e)&&Ni(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(Hi(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=Li(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),o=!e&&this.host._bindingHasHaPage(t.binding),a=this._markerPreviewDevice(t),n=this._effectiveMarkerTapAction(t,a),r=a?Es(this.host._serverCfg?.spaces.find(t=>t.id===a.space)):null,l=a?this._markerPreviewDevices(a):this.host._devices,h="toggle"===n&&a?this._toggleIntent(a,l):null,c=this._toggleHintLines(h),u=a?Ei(this.host._planHass,a,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:r?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(a.id),lightDevices:l,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,_=a?Ms(this.host._planHass,a,l):[],m=a?Ds(this.host._planHass,a,_):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!u?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:u?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:u?.valueBadge?.position||t.valueBadgePosition,v=Oi(f),y=!!f&&!_.some(t=>t.key===v),$=_.find(t=>t.key===v),w=Oi(t.valueSource),k=!(!t.valueSource||w&&_.some(t=>t.key===w)),x=_.find(t=>t.key===w),S=u?.valueSource?.sourceKey||"",M=this._markerAutoHasSpatialSource(t),D=!!a&&Bi(this.host._planHass,{...a}),C=Wi(t.lightRole,M,D,t.glowMode),P=!C.sourceExists,I=!C.fromSourceEnabled,T=C.passive,z=C.effectiveMode,R="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||M?T?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),A=a?Ai(a):[],F=a&&ji(a)||"",N=!!t.lightEntity&&!A.includes(t.lightEntity),H=a?Gi(a):[],L=!!t.toggleEntity&&!H.includes(t.toggleEntity),E="toggle"===n?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,O=E?[...E.targets,...E.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",B=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return p`this._closeMarkerDialog()}> +
    + ${"ha_disabled"===i?.kind?p`
    + + ${this.host._t(`marker.ha_disabled_${i.reason}`)} + ${o?p``:d} +
    `:"unverified"===i?.kind&&t.binding?p`
    + + ${this.host._t("marker.ha_registry_limited")} +
    `:d} + + this.host._markerDialog={...t,name:e.target.value}} /> + + +
    + +
    + + +
    + ${"ha"===t.bindingMode?p` + ${t.bindingOpen?p`
    + this.host._markerDialog={...t,bindingFilter:e.target.value}} /> +
    + ${s.map(e=>p`
    {if(e.value===t.binding)return void(this.host._markerDialog={...t,bindingOpen:!1});const s={...t,binding:e.value,bindingOpen:!1,controls:Cs(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value),valueSource:null,valueSourceTouched:!0})}}> + ${e.label}${e.sub} +
    `)} + ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} +
    +
    `:d}`:d} +
    + + + + + ${this._renderVacSection(t)} + + + + ${"toggle"===n&&(H.length>1||L)?p`
    +
    + + ${this._help("marker.toggle_entity.help")} +
    + + ${L?p`

    + + ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:O||this.host._t("marker.toggle_entity_none")})} +

    `:d} +
    `:d} + ${"toggle"===n?p`
    + ${c.map(t=>p`
    ${t}
    `)} +
    +
    ${t.tapHintAnnouncement}
    `:d} + ${"run"===n?(()=>{const e=t.runFilter.trim().toLowerCase(),s=this._runCandidates().filter(t=>!e||t.label.toLowerCase().includes(e)||t.value.includes(e)),i=t.tapTarget?this._runCandidates().find(e=>e.value===t.tapTarget):null;return p` + + ${t.tapTarget&&!i?p`
    ${this.host._t("marker.run_target_gone",{id:t.tapTarget})}
    `:d} + {t.target.select()}} + @input=${e=>this.host._markerDialog={...t,runFilter:e.target.value,tapTarget:""}} /> + ${i?d:p`
    + ${s.slice(0,40).map(e=>p`
    this.host._markerDialog={...t,tapTarget:e.value,runFilter:""}}> + ${e.label}${e.sub} +
    `)} + ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} +
    `}`})():d} + ${"run"===n||"toggle"===n?p``:d} + +
    + + ${this._help("marker.controls.help")} +
    + ${t.controls.length?p`
    + ${t.controls.map(e=>{const s=this._controlRefInfo(e);return p` + ${s.label} + this.host._markerDialog=this._announceToggleDraft({...t,controls:t.controls.filter(t=>t!==e)})}> + `})} +
    `:d} + this.host._markerDialog={...t,controlsFilter:e.target.value}} /> + ${t.controlsFilter.trim()?p`
    + ${this._controlCandidates(t).map(e=>p``)} +
    `:d} + + ${this.host._bindingHasClimate(t.binding)?p``:d} +
    + ${this.host._t("marker.light_role_label")}${this._help("marker.light_role.help")} +
    + + + +
    +
    + + ${"always"===t.lightRole&&(A.length>1||N)?p`
    +
    + + ${this._help("marker.light_entity.help")} +
    + + ${N?p`

    + + ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:F||"—"})} +

    `:d} +
    `:d} + +
    + ${this.host._t("marker.glow_color_label")}${this._help("marker.glow_mode.help")} +
    + + + +
    + ${"auto"!==z?p`
    + {this.host._markerDialog={...t,glowMode:z,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> + ${"fixed"===z?p` + ${this.host._t("marker.glow_brightness")} + ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:z,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},P,this.host._t("marker.glow_brightness"))} + ${Math.round(t.glowBrightness)}%`:d} +
    `:d} +
    +
    +
    + + ${this._help("marker.glow_radius.help")} +
    +
    + this.host._markerDialog={...t,glowRadius:e.target.value}} /> + ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} +
    +
    + ${P||T?p`

    + ${R} +

    `:d} + + + ${customElements.get("ha-icon-picker")?p`{const s=e.detail.value||"";(t.icon||s!==t.autoIcon)&&(this.host._markerDialog={...t,icon:s})}}>`:p`this.host._markerDialog={...t,icon:e.target.value}} />`} + ${!t.icon&&t.autoIcon?p`

    + ${this.host._t("marker.icon_auto",{icon:t.autoIcon})} +

    `:d} + + + +

    ${this.host._t(Hh[t.display])}

    + ${"value"===t.display?p`
    +
    + + ${this._help("marker.value_source.help")} +
    + + ${x?p`

    ${x.technical}

    `:d} + ${e?p`

    + + ${this.host._t("marker.preview.reason.value_virtual")} +

    `:k?p`

    + ${this.host._t("marker.value_source_missing_hint")} +

    `:d} +
    `:d} + ${"static_icon"===t.display&&this.host._bindingHasAlarm(t.binding)?p`
    + + ${this.host._t("marker.static_alarm_warning")} +
    `:d} +
    + ${this.host._t("marker.value_badge_title")}${this._help("marker.value_badge.help")} + + ${"static_icon"===t.display?p`

    + ${this.host._t("marker.value_badge_static")} +

    `:_.length||t.valueBadgeSource?d:p`

    + ${this.host._t("marker.value_badge_empty")} +

    `} + ${g?p` +
    + + ${this._help("marker.value_badge_source.help")} +
    + + ${$?p`

    ${$.technical}

    `:d} + ${y?p`

    + ${this.host._t("marker.value_badge_missing_hint")} +

    `:d} + ${"value"===t.display&&v===S?p`

    + ${this.host._t("marker.value_badge_duplicate")} +

    `:d} +
    + + ${this._help("marker.value_badge_position.help")} +
    + + `:d} +
    + ${u?p` + `:p`
    + + ${this.host._t("marker.preview.select_source")} +
    `} + ${"icon_ripple"===t.display?p`
    + {this.host._markerDialog={...t,rippleColor:e.detail.color}}}> +
    +
    + ${this.host._t("marker.ripple_size")} + ${this._rangeInput(1,8,.5,t.rippleSize,e=>this.host._markerDialog={...t,rippleSize:e})} + ×${t.rippleSize} +
    +

    ${this.host._t("marker.activity_alarm_note")}

    `:d} + + +
    + ${this._rangeInput(.5,3,.1,t.size,e=>this.host._markerDialog={...t,size:e})} + ×${t.size.toFixed(1)} + ${this.host._t("marker.angle_label")} + ${""} + ${this._rangeInput(0,355,5,t.angle,e=>this.host._markerDialog={...t,angle:e})} + ${t.angle}° +
    + + + this.host._markerDialog={...t,model:e.target.value}} /> + + + this.host._markerDialog={...t,link:e.target.value}} /> + + + + + +
    + ${t.pdfs.map(t=>p` + ${t.name} + this._removeMarkerPdf(t.url)}>`)} + + + this._pickMarkerFiles(t)} /> + +
    +
    +
    +
    + ${t.devId?p``:d} + ${t.devId?p``:d} +
    +
    + + +
    +
    +
    `}_renderSpaceDialog(){const t=this.host._spaceDialog,e=this.host._importTotal>0&&"create"===t.mode?this.host._t("import.progress",{i:this.host._importTotal-this.host._importQueue.length,n:this.host._importTotal}):"",s=()=>{this.host._spaceDialog=null,this.host._importQueue=[],this.host._importTotal=0};return p` +
    + + this.host._spaceDialog={...t,title:e.target.value}} /> + + + ${"file"===t.source?p`
    + ${t.planFile?p`${t.planFile.name}`:t.planUrl?p`${this.host._t("space.plan_alt")}`:p`${this.host._t("space.no_plan")}`} + + + this._pickPlanFile(t)} /> + + +
    + ${t.pickSaved?this._renderServerPlans(t):d}`:d} + + +
    + + ${this._help("space.cell_cm.help")} +
    +
    + {const s=e.target.value,i=Oh(s),o=null==i?null:Ji(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(Eh,o)):t.cellCm}}} /> + ${this.host._t(this.host._imperial?"space.scale_unit_imperial":"space.scale_unit")} +
    + + + +
    + + ${this._help("space.zero_wall_style.help")} +
    + + + + ${""} + +
    ${this.host._t("space.hide_decor_tip")}
    + +
    ${this.host._t("space.hide_openings_tip")}
    + + ${[["labelTemp","space.label_temp"],["labelHum","space.label_hum"],["labelLqi","space.label_lqi"],["labelLight","space.label_light"]].map(([e,s])=>p``)} + +
    + ${this._rangeInput(50,300,5,Math.round(100*t.cardFontScale),e=>this.host._spaceDialog={...t,cardFontScale:e/100})} + ${Math.round(100*t.cardFontScale)}% +
    + ${this.host._renderCardPreview(t.cardFontScale,1,1)} +
    + {this.host._spaceDialog={...t,roomColor:e.detail.color,roomOpacity:e.detail.opacity}}}> +
    +
    + + ${this._help("space.bg_mode.help")} +
    + + ${"static"===(t.bgMode??ae(this.host._settings,{}))?p`
    + {this.host._spaceDialog={...t,bgColor:e.detail.color}}}> + ${t.bgColor?p``:p`${this.host._t("space.bg_inherited")}`} +
    `:d} +
    + + ${this._help("space.north.help")} +
    +
    + {const s=e.target.value.trim(),i=""===s?null:Math.round(Number(s));this.host._spaceDialog={...t,northDeg:null!==i&&Number.isFinite(i)?Math.min(359,Math.max(0,i)):null}}} /> + ${null===t.northDeg?this.host._t("space.north_inherited",{v:null===ne(this.host._settings,{})?"—":String(ne(this.host._settings,{}))+"°"}):"°"} +
    + + +
    + ${this.host._t("space.fill_label")} + ${this._help("space.fill_mode.help")} +
    + ${Yi.map(t=>[t,"fill."+t]).map(([e,s])=>p` + ${"custom"===e&&"custom"===t.fillMode?p`
    + ${this.host._t("space.custom_fill")} + {this.host._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}> + ${t.customFill?p``:d} +
    `:d}`)} + + ${t.deleteBlockers?p``:d} +
    + +
    `}_renderMergeDialog(){const t=this.host._mergeDialog,e=this.host._spaceModel()?.rooms||[],s=(s,i)=>{const o=e.find(t=>t.id===s),a=o?.area?this.host.hass.areas[o.area]?.name:null;return p``};return p`this.host._mergeDialog=null}> +
    +

    ${this.host._t("merge.hint")}

    + + ${s(t.aId,"a")} + ${s(t.bId,"b")} +
    +
    + + + +
    +
    `}_renderRoomSource(t){const e="temp"===t?this.host._roomTempSrc:this.host._roomHumSrc,s=e=>{"temp"===t?this.host._roomTempSrc=e:this.host._roomHumSrc=e,this.host.requestUpdate()},i=this.host._roomSrcOpen===t;return p` + + + + ${e||i?p` + ${i?p`
    + {this.host._roomSrcFilter=t.target.value,this.host.requestUpdate()}} /> +
    + ${this._roomSrcCandidates().map(t=>p`
    {s(t.value),this.host._roomSrcOpen=null}}> + ${t.label}${t.sub} +
    `)} +
    +
    `:d}`:d}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),o=Es(this.host._curSpaceCfg),a=this.host._roomFill||o.fill,n=this.host._roomCustomFill||o.customFill,r=[...this.host._freeAreas];if(t&&this.host._areaSel&&!r.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&r.unshift(t)}return p`this._roomDialogCancel()}> +
    + ${s?p`

    + ${s} +

    `:d} + + this.host._nameSel=t.target.value} /> + + + + + + ${[["","fill.inherit"],...Xi.map(t=>[t,"fill."+t])].map(([t,e])=>p``)} + ${"custom"===a?p`
    + ${this.host._roomCustomFill?this.host._t("room.custom_fill_own"):this.host._t("room.custom_fill_space")} + {this.host._roomCustomFill={c:t.detail.color,a:t.detail.opacity}}}> + ${this.host._roomCustomFill?p``:d} +
    `:d} + ${this._renderRoomSource("temp")} + ${this._renderRoomSource("hum")} + + + +
    + ${this._rangeInput(50,300,5,Math.round(100*this.host._roomNameScale),t=>{this.host._roomNameScale=t/100,this.host.requestUpdate()})} + ${Math.round(100*this.host._roomNameScale)}% +
    + +
    + ${this._rangeInput(50,300,5,Math.round(100*this.host._roomLabelScale),t=>{this.host._roomLabelScale=t/100,this.host.requestUpdate()})} + ${Math.round(100*this.host._roomLabelScale)}% +
    + ${this.host._renderCardPreview(Es(this.host._curSpaceCfg).cardFontScale,this.host._roomNameScale,this.host._roomLabelScale)} +
    +
    + + + ${t?p``:p`${this.host._pendingSplit?d:p``} + `} +
    +
    `}}export{qh as EDITOR_RUNTIME_FINGERPRINT,Uh as HouseplanEditorRuntime}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js deleted file mode 100644 index 0fd818b4..00000000 --- a/custom_components/houseplan/frontend/houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js +++ /dev/null @@ -1,2911 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{e as t,i as e,E as s,s as i,a as o,n as a,f as n,b as r,l,o as h,r as c,A as d,c as p,d as u,g as _,h as m,j as g,k as f,m as b,p as v,t as y,w as $,q as w,u as k,v as x,x as S,y as M,z as D,B as C,C as P,D as I,F as T,G as R,H as z,I as A,J as F,K as N,L as H,M as L,N as E,O,P as B,Q as W,R as j,S as G,T as q,U,V as K,W as V,X as J,Y,Z,_ as X,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as lt,a9 as ht,aa as ct,ab as dt,ac as pt,ad as ut,ae as _t,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as Rt,ax as zt,ay as At,az as Ft,aA as Nt,aB as Ht,aC as Lt,aD as Et,aE as Ot,aF as Bt,aG as Wt,aH as jt,aI as Gt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Zt,aQ as Xt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as le,a$ as he,b0 as ce,b1 as de,b2 as pe,b3 as ue,b4 as _e,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as ze,bo as Ae,bp as Fe,bq as Ne,br as He,bs as Le,bt as Ee,bu as Oe,bv as Be,bw as We,bx as je,by as Ge,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Ze,bG as Xe,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as ls,bR as hs,bS as cs,bT as ds,bU as ps,bV as us,bW as _s,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as zs,ce as As,cf as Fs,cg as Ns,ch as Hs,ci as Ls,cj as Es,ck as Os,cl as Bs,cm as Ws,cn as js,co as Gs,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Zs,cw as Xs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as li,cH as hi,cI as ci,cJ as di,cK as pi,cL as ui,cM as _i,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di,c_ as Ci,c$ as Pi,d0 as Ii,d1 as Ti,d2 as Ri,d3 as zi,d4 as Ai,d5 as Fi,d6 as Ni,d7 as Hi,d8 as Li,d9 as Ei,da as Oi,db as Bi,dc as Wi,dd as ji,de as Gi,df as qi,dg as Ui,dh as Ki,di as Vi,dj as Ji,dk as Yi,dl as Zi,dm as Xi}from"./houseplan-card-DcH_msOh.js";import{F as Qi,f as to,p as eo,c as so,i as io,e as oo,r as ao,a as no,b as ro,s as lo,d as ho,t as co}from"./backdrop-pick-CXzP7hUG.js";import{t as po,m as uo,T as _o}from"./zigbee-topology-x104QxRs.js";const mo={},go=t(class extends e{constructor(){super(...arguments),this.ot=mo}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}});var fo={"gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.show_room_tooltip":"Show the room information window on hover","support.about_group":"About the card","support.guide_group":"Documentation","support.guide":"User guide","support.form_group":"Send a report or suggestion","support.contact":"Contact details (email/tg/WhatsApp), optional.","support.message":"Message","support.attach":"Attach anonymized information from your plan","support.attach_hint":"Includes only allowed plan geometry, settings and bounded runtime facts. Excludes names, Home Assistant IDs, live states, URLs, files, and your message/contact.","support.geometry_warning":"The package contains no names or Home Assistant IDs, but it does contain the exact geometry and dimensions of your home.","support.close_busy_title":"Stop the current operation?","support.close_busy_body":"House Plan is preparing or sending the report. Closing now stops waiting for the result.","support.building":"Preparing anonymized data…","support.preview_summary":"Package v{version} · {spaces} spaces · {size} KiB","support.prepared":"Snapshot prepared {n} min ago","support.show_data":"Show data","support.download":"Download JSON","support.refresh":"Refresh snapshot","support.privacy":"When you send, your message, optional contact and, if selected, exact geometry leave your Home Assistant through the House Plan project relay and are visible to maintainers. The project node keeps the report for up to 30 days. Network infrastructure sees your HA server address, but House Plan does not retain it raw; rate-limit and idempotency metadata is kept for up to 24 hours.","support.error_title":"The report was not sent","support.manual_recovery":"You can keep the message and attachment and continue manually:","support.copy_message":"Copy message","support.success":"Report sent: {id}. Save this number when contacting support.","support.copy_id":"Copy ID","support.sending":"Sending…","support.retry":"Retry","support.send":"Send","support.update_required":"Update the House Plan card and integration to compatible versions to send reports.","support.message_copied":"Message copied","support.id_copied":"Report ID copied","support.copy_failed":"Could not access the clipboard. Select and copy the text manually.","support.validation.message_required":"Enter a message.","support.validation.message_too_long":"The message must contain no more than 10,000 characters.","support.validation.contact_too_long":"Contact details must contain no more than 320 characters.","support.validation.preview_missing":"Wait for the attachment preview or turn the attachment off.","support.validation.preview_expired":"The attachment preview expired. Refresh the snapshot.","support.error.support_invalid_message":"Check the message and try again.","support.error.support_preview_expired":"The attachment preview expired or is no longer available. Refresh the snapshot.","support.error.support_package_too_large":"The anonymized package exceeds the 8 MiB limit.","support.error.support_rate_limited":"Too many reports or previews were requested. Try again later.","support.error.support_unavailable":"The private support service is temporarily unavailable. Your draft remains here.","support.error.support_rejected":"The report did not pass the support service validation.","support.error.unauthorized":"You do not have permission to send this report.","vac.routes_section":"Maps and floors","vac.routes_current_map":"Current map","vac.routes_no_map":"no telemetry","vac.routes_status":"Routing","vac.routes_state_ready":"map assigned and calibrated","vac.routes_state_needs_calibration":"map assigned, calibration needed","vac.routes_state_unmapped":"current map is not assigned to a floor","vac.routes_state_ambiguous":"two maps are indistinguishable — no floor is chosen","vac.routes_state_missing_space":"the map's space was deleted","vac.routes_state_none":"no map data","vac.routes_empty":"No map is assigned to a floor yet.","vac.route_map_default":"no map id","vac.route_active":"Active","vac.route_space":"Space","vac.route_space_none":"not selected","vac.route_status_ready":"ready","vac.route_status_needs_calibration":"needs calibration","vac.route_status_ambiguous":"ambiguous","vac.route_status_missing_space":"space deleted","vac.route_missing_space_group":"Deleted space","vac.route_add_current":"Add current map","vac.route_delete":"Remove map","vac.route_delete_title":"Remove the map from its floor?","vac.route_delete_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_delete_confirm":"Remove","vac.route_space_title":"Move the map to another space?","vac.route_space_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_space_confirm":"Move","vac.route_missing_space_hint":"One of the maps points at a deleted space: pick a new one or remove the map. Until then the robot is not drawn for that map.","vac.route_default_hint":"The source reports no stable map id. Several floors cannot be told apart without another source or the robot's own selected map.","vac.route_target_hint":"The dock stays in the “{space}” space regardless of the current map.","vac.route_add_source":"Add a map source","vac.route_add_source_hint":"For integrations that give each map its own camera. The “All cameras” section above widens this list.","vac.route_source_no_map":"no map reported"};const bo={en:fo,ru:{"gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.show_room_tooltip":"Показывать окно с информацией при наведении на комнату","support.about_group":"О карточке","support.guide_group":"Документация","support.guide":"Руководство пользователя","support.form_group":"Отправить репорт или предложение","support.contact":"Контакт для связи (email/tg/WhatsApp), необязательно.","support.message":"Сообщение","support.attach":"Прикрепить обезличенную информацию из вашего плана","support.attach_hint":"В пакет входят только разрешённые геометрия, настройки и ограниченные сведения о среде. Не входят имена, HA id, текущие состояния, URL, файлы, сообщение и контакт.","support.geometry_warning":"Пакет не содержит имён и HA id, но содержит точную геометрию и размеры дома.","support.close_busy_title":"Прервать текущую операцию?","support.close_busy_body":"House Plan подготавливает или отправляет репорт. При закрытии ожидание результата прекратится.","support.building":"Подготавливаем обезличенные данные…","support.preview_summary":"Пакет v{version} · пространств: {spaces} · {size} КиБ","support.prepared":"Снимок подготовлен {n} мин назад","support.show_data":"Показать данные","support.download":"Скачать JSON","support.refresh":"Обновить снимок","support.privacy":"При отправке сообщение, необязательный контакт и, если выбран пакет, точная геометрия покидают ваш Home Assistant через relay проекта House Plan и доступны мейнтейнерам. Узел проекта хранит репорт до 30 дней. Сетевая инфраструктура видит адрес сервера HA, но House Plan не сохраняет его в исходном виде; метаданные лимитов и идемпотентности хранятся до 24 часов.","support.error_title":"Репорт не отправлен","support.manual_recovery":"Сообщение и вложение можно сохранить и продолжить вручную:","support.copy_message":"Копировать сообщение","support.success":"Репорт отправлен: {id}. Сохраните номер для связи с поддержкой.","support.copy_id":"Копировать ID","support.sending":"Отправляем…","support.retry":"Повторить","support.send":"Отправить","support.update_required":"Обновите карточку и интеграцию House Plan до совместимых версий, чтобы отправлять репорты.","support.message_copied":"Сообщение скопировано","support.id_copied":"ID репорта скопирован","support.copy_failed":"Не удалось получить доступ к буферу обмена. Выделите и скопируйте текст вручную.","support.validation.message_required":"Введите сообщение.","support.validation.message_too_long":"Сообщение должно содержать не более 10 000 символов.","support.validation.contact_too_long":"Контакт должен содержать не более 320 символов.","support.validation.preview_missing":"Дождитесь предпросмотра вложения или отключите его.","support.validation.preview_expired":"Предпросмотр вложения истёк. Обновите снимок.","support.error.support_invalid_message":"Проверьте сообщение и повторите попытку.","support.error.support_preview_expired":"Предпросмотр вложения истёк или больше недоступен. Обновите снимок.","support.error.support_package_too_large":"Обезличенный пакет превышает лимит 8 МиБ.","support.error.support_rate_limited":"Запрошено слишком много репортов или предпросмотров. Повторите позже.","support.error.support_unavailable":"Закрытая служба поддержки временно недоступна. Черновик сохранён в этом окне.","support.error.support_rejected":"Репорт не прошёл проверку службы поддержки.","support.error.unauthorized":"У вас нет прав на отправку этого репорта.","vac.routes_section":"Карты и этажи","vac.routes_current_map":"Текущая карта","vac.routes_no_map":"источник молчит","vac.routes_status":"Маршрутизация","vac.routes_state_ready":"карта сопоставлена и откалибрована","vac.routes_state_needs_calibration":"карта сопоставлена, нужна калибровка","vac.routes_state_unmapped":"текущая карта не сопоставлена этажу","vac.routes_state_ambiguous":"две карты неразличимы — этаж не выбирается","vac.routes_state_missing_space":"пространство карты удалено","vac.routes_state_none":"нет данных о карте","vac.routes_empty":"Ни одна карта ещё не сопоставлена этажу.","vac.route_map_default":"без идентификатора","vac.route_active":"Текущая","vac.route_space":"Пространство","vac.route_space_none":"не выбрано","vac.route_status_ready":"готова","vac.route_status_needs_calibration":"нужна калибровка","vac.route_status_ambiguous":"неоднозначна","vac.route_status_missing_space":"пространство удалено","vac.route_missing_space_group":"Пространство удалено","vac.route_add_current":"Добавить текущую карту","vac.route_delete":"Удалить карту","vac.route_delete_title":"Удалить карту с этажа?","vac.route_delete_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_delete_confirm":"Удалить","vac.route_space_title":"Перенести карту в другое пространство?","vac.route_space_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_space_confirm":"Перенести","vac.route_missing_space_hint":"Одна из карт ссылается на удалённое пространство: выберите новое или удалите карту. Пока ссылка не исправлена, робот на этой карте не рисуется.","vac.route_default_hint":"Источник не сообщает стабильный идентификатор карты. Несколько этажей нельзя различить без другого источника или собственной карты робота.","vac.route_target_hint":"Док остаётся в пространстве «{space}» независимо от текущей карты.","vac.route_add_source":"Добавить источник карты","vac.route_add_source_hint":"Для интеграций, где у каждой карты своя камера. Список расширяется разделом «Все камеры» выше.","vac.route_source_no_map":"карта не читается"},de:{"gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.show_room_tooltip":"Rauminformationen beim Darüberfahren anzeigen","support.about_group":"Über die Karte","support.guide_group":"Dokumentation","support.guide":"Benutzerhandbuch","support.form_group":"Bericht oder Vorschlag senden","support.contact":"Kontakt (E-Mail/tg/WhatsApp), optional.","support.message":"Nachricht","support.attach":"Anonymisierte Informationen aus dem Plan anhängen","support.attach_hint":"Enthält nur erlaubte Plangeometrie, Einstellungen und begrenzte Laufzeitdaten. Namen, Home-Assistant-IDs, Live-Zustände, URLs, Dateien, Nachricht und Kontakt werden ausgeschlossen.","support.geometry_warning":"Das Paket enthält keine Namen oder Home-Assistant-IDs, aber die exakte Geometrie und die Maße Ihres Hauses.","support.close_busy_title":"Aktuellen Vorgang abbrechen?","support.close_busy_body":"House Plan bereitet den Bericht vor oder sendet ihn. Beim Schließen wird nicht weiter auf das Ergebnis gewartet.","support.building":"Anonymisierte Daten werden vorbereitet…","support.preview_summary":"Paket v{version} · {spaces} Bereiche · {size} KiB","support.prepared":"Schnappschuss vor {n} Min. erstellt","support.show_data":"Daten anzeigen","support.download":"JSON herunterladen","support.refresh":"Schnappschuss aktualisieren","support.privacy":"Beim Senden verlassen Ihre Nachricht, der optionale Kontakt und, falls ausgewählt, die exakte Geometrie Ihren Home Assistant über das House-Plan-Projektrelay und sind für die Maintainer sichtbar. Der Projektserver speichert den Bericht bis zu 30 Tage. Die Netzwerkinfrastruktur sieht die Adresse Ihres HA-Servers, House Plan speichert sie jedoch nicht im Rohformat; Limit- und Idempotenzdaten bleiben bis zu 24 Stunden erhalten.","support.error_title":"Der Bericht wurde nicht gesendet","support.manual_recovery":"Nachricht und Anhang können gespeichert und manuell weitergegeben werden:","support.copy_message":"Nachricht kopieren","support.success":"Bericht gesendet: {id}. Bewahren Sie diese Nummer für den Support auf.","support.copy_id":"ID kopieren","support.sending":"Wird gesendet…","support.retry":"Erneut versuchen","support.send":"Senden","support.update_required":"Aktualisieren Sie House-Plan-Karte und Integration auf kompatible Versionen, um Berichte zu senden.","support.message_copied":"Nachricht kopiert","support.id_copied":"Berichts-ID kopiert","support.copy_failed":"Kein Zugriff auf die Zwischenablage. Markieren und kopieren Sie den Text manuell.","support.validation.message_required":"Geben Sie eine Nachricht ein.","support.validation.message_too_long":"Die Nachricht darf höchstens 10.000 Zeichen enthalten.","support.validation.contact_too_long":"Der Kontakt darf höchstens 320 Zeichen enthalten.","support.validation.preview_missing":"Warten Sie auf die Vorschau oder deaktivieren Sie den Anhang.","support.validation.preview_expired":"Die Anhangsvorschau ist abgelaufen. Aktualisieren Sie den Schnappschuss.","support.error.support_invalid_message":"Prüfen Sie die Nachricht und versuchen Sie es erneut.","support.error.support_preview_expired":"Die Anhangsvorschau ist abgelaufen oder nicht mehr verfügbar. Aktualisieren Sie den Schnappschuss.","support.error.support_package_too_large":"Das anonymisierte Paket überschreitet das Limit von 8 MiB.","support.error.support_rate_limited":"Zu viele Berichte oder Vorschauen wurden angefordert. Versuchen Sie es später erneut.","support.error.support_unavailable":"Der private Supportdienst ist vorübergehend nicht verfügbar. Der Entwurf bleibt in diesem Fenster erhalten.","support.error.support_rejected":"Der Bericht hat die Prüfung des Supportdienstes nicht bestanden.","support.error.unauthorized":"Sie dürfen diesen Bericht nicht senden.","vac.routes_section":"Karten und Etagen","vac.routes_current_map":"Aktuelle Karte","vac.routes_no_map":"keine Telemetrie","vac.routes_status":"Zuordnung","vac.routes_state_ready":"Karte zugeordnet und kalibriert","vac.routes_state_needs_calibration":"Karte zugeordnet, Kalibrierung nötig","vac.routes_state_unmapped":"aktuelle Karte keiner Etage zugeordnet","vac.routes_state_ambiguous":"zwei Karten nicht unterscheidbar — keine Etage gewählt","vac.routes_state_missing_space":"der Raum der Karte wurde gelöscht","vac.routes_state_none":"keine Kartendaten","vac.routes_empty":"Noch keine Karte einer Etage zugeordnet.","vac.route_map_default":"ohne Karten-ID","vac.route_active":"Aktiv","vac.route_space":"Raum","vac.route_space_none":"nicht gewählt","vac.route_status_ready":"bereit","vac.route_status_needs_calibration":"Kalibrierung nötig","vac.route_status_ambiguous":"mehrdeutig","vac.route_status_missing_space":"Raum gelöscht","vac.route_missing_space_group":"Gelöschter Bereich","vac.route_add_current":"Aktuelle Karte hinzufügen","vac.route_delete":"Karte entfernen","vac.route_delete_title":"Karte von der Etage entfernen?","vac.route_delete_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_delete_confirm":"Entfernen","vac.route_space_title":"Karte in einen anderen Raum verschieben?","vac.route_space_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_space_confirm":"Verschieben","vac.route_missing_space_hint":"Eine Karte verweist auf einen gelöschten Raum: neuen wählen oder Karte entfernen. Bis dahin wird der Roboter für diese Karte nicht gezeichnet.","vac.route_default_hint":"Die Quelle meldet keine stabile Karten-ID. Mehrere Etagen lassen sich ohne andere Quelle oder die vom Roboter gewählte Karte nicht unterscheiden.","vac.route_target_hint":"Die Station bleibt unabhängig von der aktuellen Karte im Raum „{space}“.","vac.route_add_source":"Kartenquelle hinzufügen","vac.route_add_source_hint":"Für Integrationen, die jeder Karte eine eigene Kamera geben. Der Abschnitt „Alle Kameras“ oben erweitert diese Liste.","vac.route_source_no_map":"keine Karte gemeldet"},fr:{"gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.show_room_tooltip":"Afficher les informations de la pièce au survol","support.about_group":"À propos de la carte","support.guide_group":"Documentation d’aide","support.guide":"Guide utilisateur","support.form_group":"Envoyer un rapport ou une suggestion","support.contact":"Contact (e-mail/tg/WhatsApp), facultatif.","support.message":"Votre message","support.attach":"Joindre des informations anonymisées de votre plan","support.attach_hint":"Inclut uniquement la géométrie, les réglages autorisés et des données d’exécution limitées. Exclut les noms, identifiants Home Assistant, états en direct, URL, fichiers, message et contact.","support.geometry_warning":"Le paquet ne contient ni noms ni identifiants Home Assistant, mais il contient la géométrie et les dimensions exactes de votre logement.","support.close_busy_title":"Interrompre l’opération en cours ?","support.close_busy_body":"House Plan prépare ou envoie le rapport. Fermer maintenant arrête l’attente du résultat.","support.building":"Préparation des données anonymisées…","support.preview_summary":"Paquet v{version} · {spaces} espaces · {size} Kio","support.prepared":"Instantané préparé il y a {n} min","support.show_data":"Afficher les données","support.download":"Télécharger le JSON","support.refresh":"Actualiser l’instantané","support.privacy":"Lors de l’envoi, votre message, le contact facultatif et, si elle est sélectionnée, la géométrie exacte quittent votre Home Assistant via le relais du projet House Plan et sont visibles par les mainteneurs. Le serveur du projet conserve le rapport jusqu’à 30 jours. L’infrastructure réseau voit l’adresse de votre serveur HA, mais House Plan ne la conserve pas en clair ; les métadonnées de limitation et d’idempotence sont gardées jusqu’à 24 heures.","support.error_title":"Le rapport n’a pas été envoyé","support.manual_recovery":"Vous pouvez conserver le message et la pièce jointe puis continuer manuellement :","support.copy_message":"Copier le message","support.success":"Rapport envoyé : {id}. Conservez ce numéro pour contacter l’assistance.","support.copy_id":"Copier l’ID","support.sending":"Envoi…","support.retry":"Réessayer","support.send":"Envoyer","support.update_required":"Mettez à jour la carte et l’intégration House Plan vers des versions compatibles pour envoyer des rapports.","support.message_copied":"Message copié","support.id_copied":"ID du rapport copié","support.copy_failed":"Impossible d’accéder au presse-papiers. Sélectionnez et copiez le texte manuellement.","support.validation.message_required":"Saisissez un message.","support.validation.message_too_long":"Le message ne doit pas dépasser 10 000 caractères.","support.validation.contact_too_long":"Le contact ne doit pas dépasser 320 caractères.","support.validation.preview_missing":"Attendez l’aperçu de la pièce jointe ou désactivez-la.","support.validation.preview_expired":"L’aperçu de la pièce jointe a expiré. Actualisez l’instantané.","support.error.support_invalid_message":"Vérifiez le message et réessayez.","support.error.support_preview_expired":"L’aperçu de la pièce jointe a expiré ou n’est plus disponible. Actualisez l’instantané.","support.error.support_package_too_large":"Le paquet anonymisé dépasse la limite de 8 Mio.","support.error.support_rate_limited":"Trop de rapports ou d’aperçus ont été demandés. Réessayez plus tard.","support.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible. Le brouillon reste dans cette fenêtre.","support.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","support.error.unauthorized":"Vous n’êtes pas autorisé à envoyer ce rapport.","vac.routes_section":"Cartes et étages","vac.routes_current_map":"Carte actuelle","vac.routes_no_map":"aucune télémétrie","vac.routes_status":"Routage","vac.routes_state_ready":"carte assignée et calibrée","vac.routes_state_needs_calibration":"carte assignée, calibrage requis","vac.routes_state_unmapped":"carte actuelle non assignée à un étage","vac.routes_state_ambiguous":"deux cartes indiscernables — aucun étage choisi","vac.routes_state_missing_space":"l'espace de la carte a été supprimé","vac.routes_state_none":"aucune donnée de carte","vac.routes_empty":"Aucune carte n'est encore assignée à un étage.","vac.route_map_default":"sans identifiant","vac.route_active":"Active","vac.route_space":"Espace","vac.route_space_none":"non sélectionné","vac.route_status_ready":"prête","vac.route_status_needs_calibration":"calibrage requis","vac.route_status_ambiguous":"ambiguë","vac.route_status_missing_space":"espace supprimé","vac.route_missing_space_group":"Espace supprimé","vac.route_add_current":"Ajouter la carte actuelle","vac.route_delete":"Supprimer la carte","vac.route_delete_title":"Retirer la carte de son étage ?","vac.route_delete_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_delete_confirm":"Supprimer","vac.route_space_title":"Déplacer la carte vers un autre espace ?","vac.route_space_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_space_confirm":"Déplacer","vac.route_missing_space_hint":"Une carte pointe vers un espace supprimé : choisissez-en un autre ou supprimez la carte. D'ici là, le robot n'est pas dessiné pour cette carte.","vac.route_default_hint":"La source ne fournit pas d'identifiant de carte stable. Plusieurs étages ne peuvent pas être distingués sans une autre source ou la carte sélectionnée du robot.","vac.route_target_hint":"La station reste dans l'espace « {space} » quelle que soit la carte actuelle.","vac.route_add_source":"Ajouter une source de carte","vac.route_add_source_hint":"Pour les intégrations qui donnent à chaque carte sa propre caméra. La section « Toutes les caméras » ci-dessus élargit cette liste.","vac.route_source_no_map":"aucune carte signalée"}};function vo(t,e,s){return i(bo[t]?.[e]??fo[e]??e,s)}function yo(t,e,s,i,o){return{markerId:t,source:i,mapId:o,space:e.length?"":s}}function $o(t,e,s){return{...t,space:s.has(e)?e:""}}function wo(t,e=Math.random){const s=new Set(t);for(let t=0;t<64;t+=1){const t="vr_"+Math.floor(16777215*e()).toString(36).padStart(5,"0");if(!s.has(t))return t}return"vr_"+Date.now().toString(36)}function ko(t,e,s){return t.map(t=>t.id===e?{...t,calibration:s}:t)}const xo=new WeakMap,So=(t,e)=>{const s=(t._serverCfg?.spaces||[]).find(t=>t?.id===e);return String(s?.name||s?.id||e)};function Mo(t,e,s){const i=t.host,n=l(i.hass,i._config?.language),r=(t,e)=>vo(n,t,e),g=e.marker?.vacuum||{},f=Array.isArray(g.map_routes),b=i._vacSource(e)||"",v=o(e.id,g,e.space,b),y=(i._serverCfg?.spaces||[]).filter(t=>"string"==typeof t?.id&&!!t.id).map(t=>({id:t.id,name:String(t.name||t.id)})),$=new Set(y.map(t=>t.id)),w=h(v,[b],t=>i._vacObservedMapId(e,t)),k=c({routes:v,observed:w,spaceIds:$}),x="ready"===k.kind||"needs_calibration"===k.kind||"missing_space"===k.kind?k.route.id:"",S=b?w[b]:void 0,M=async s=>{let o=f?g.map_routes??null:null;if(!o&&(o=function(t,e,s,i){if(!s)return null;const o=t?.calibration;if(!o||"object"!=typeof o)return null;const n=new Set,r=[];for(const t of Object.keys(o)){const l=a(o[t]);if(!l)continue;const h=i(n);n.add(h),r.push({id:h,source:s,map_id:t,space:e,calibration:l})}return r.length?r:null}(g,e.space,b,t=>wo(t))??[],!b&&Object.keys(g.calibration||{}).length))return!1;const n=s(o);return!!n&&(async s=>{const o=i._serverCfg;if(!o)return!1;const a=JSON.parse(JSON.stringify(o));a.markers=a.markers||[];let n=a.markers.find(t=>t.id===e.id);if(!n){if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return!1;n={id:e.id,binding:`${e.bindingKind}:${e.bindingRef}`,space:e.space||null,area:e.area||null,hidden:!!e.hidden},a.markers.push(n)}n.vacuum={...n.vacuum||{},map_routes:s},delete n.vacuum.calibration;const r=u(o,a,i._cfgContentFingerprint,i._cfgRev,_);i._serverCfg=a,i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._saveConfigDebounced.pending()&&i._saveConfigDebounced.cancel();try{return await t._saveConfigNow(),!0}catch(t){return m(i,r,_),i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._showToast(i._t("toast.cfg_save_failed",{err:i._errText(t)})),!1}})(n)},D=s.candidates.filter(t=>t.entityId!==b&&!v.some(e=>e.source===t.entityId)),C=async(t,e)=>{if(!e||e===t.space)return;await i._confirmDanger({key:"vacuum_route_space",kind:"warning",title:r("vac.route_space_title"),message:r("vac.route_space_body"),objectName:t.map_id||r("vac.route_map_default"),confirmLabel:r("vac.route_space_confirm"),cancelLabel:i._t("btn.cancel")})&&await M(s=>function(t,e,s,i){return t.map(t=>t.id===e?{...t,id:i,space:s,calibration:null}:t)}(s,t.id,e,wo(new Set(v.map(t=>t.id)))))},P=new Map(y.map((t,e)=>[t.id,e])),I=(t,e)=>t.map_id.localeCompare(e.map_id)||t.source.localeCompare(e.source)||t.id.localeCompare(e.id),T=v.filter(t=>$.has(t.space)).sort((t,e)=>(P.get(t.space)??0)-(P.get(e.space)??0)||I(t,e)),R=v.filter(t=>!$.has(t.space)).sort(I),z=[...T,...R],A=!!b&&void 0!==S&&!v.some(t=>t.source===b&&t.map_id===S);let F=xo.get(t);const N=F?.source||"",H=F?.mapId||"",L=!!F&&v.some(t=>t.source===N&&t.map_id===H);F&&(F.markerId!==e.id||i._vacObservedMapId(e,F.source)!==F.mapId||L&&!F.saving)&&(xo.delete(t),F=void 0),F?.space&&!$.has(F.space)&&(F=$o(F,"",$),xo.set(t,F));const E=s=>p` -
  • -
    - ${s.map_id||r("vac.route_map_default")} - ${s.id===x?p`${r("vac.route_active")}`:d} -
    - ${s.source} -
    ${(t=>$.has(t.space)?t.calibration?"ambiguous"===k.kind&&k.routeIds.includes(t.id)?r("vac.route_status_ambiguous"):r("vac.route_status_ready"):r("vac.route_status_needs_calibration"):r("vac.route_status_missing_space"))(s)}
    - -
    - - -
    -
  • `,O=`vacroute-missing-${e.id.replace(/[^a-zA-Z0-9_-]/g,"-")}`;return p` - -
    -
    -
    ${r("vac.routes_current_map")}${void 0===S?r("vac.routes_no_map"):S||r("vac.route_map_default")}
    -
    ${r("vac.routes_status")}${r(`vac.routes_state_${k.kind}`)}
    -
    - ${T.length||F?p`
      - ${F?p` -
    • -
      ${F.mapId||r("vac.route_map_default")}
      - ${F.source} - -
      - - -
      -
    • `:d} - ${T.map(E)} -
    `:d} - ${R.length?p` -
    -

    - ${r("vac.route_missing_space_group")} -

    -
      ${R.map(E)}
    -
    `:d} - ${z.length||F?d:p`
    ${r("vac.routes_empty")}
    `} -
    - -
    - ${D.length?p`
    - ${r("vac.route_add_source")} -
    ${r("vac.route_add_source_hint")}
    -
    - ${D.map(s=>{const o=i._vacObservedMapId(e,s.entityId);return p``})} -
    -
    `:d} - ${z.some(t=>!$.has(t.space))?p` -
    ${r("vac.route_missing_space_hint")}
    `:d} - ${v.some(t=>"default"===t.map_id)?p` -
    ${r("vac.route_default_hint")}
    `:d} - ${z.length?p`
    ${r("vac.route_target_hint",{space:So(i,e.space)})}
    `:d} -
    `}const Do=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Co(t){return Number.isFinite(t)?(t%360+360)%360:0}function Po(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Io(t){const e=Po(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function To(t){const e=Do(t.r,0,255)/255,s=Do(t.g,0,255)/255,i=Do(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Co(n),s:0===o?0:a/o*100,v:100*o}}function Ro(t){return function(t){const e=t=>Math.round(Do(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Co(t.h),s=Do(t.s,0,100)/100,i=Do(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}const zo={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ao extends f{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=zo,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new Qi(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Po(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=Ao.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=to(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=eo(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||zo;return{title:t.title||zo.title,hue:t.hue||zo.hue,saturation:t.saturation||zo.saturation,value:t.value||zo.value,hex:t.hex||zo.hex,invalidHex:t.invalidHex||zo.invalidHex}}_syncFromColor(t,e){const s=Po(b(t,"#607d8b"))||"#607d8b",i=Io(s);if(!i)return;const o=To(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Ro({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Po(e),i=s?Io(s):null;if(!s||!i)return;const o=To(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Po(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Po(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=Ro({h:this._hue,s:this._saturation,v:this._value}),i=Ro({h:this._hue,s:100,v:100}),o=this._labels(),a=Math.round(this._hue),n=Math.round(this._saturation),r=Math.round(this._value);return p` - `}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=b(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return p` - ${this.label?p`${this.label}`:d} - - ${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):d} - `}}Ao.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},Ao.styles=g` - :host { - position: relative; - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 0; - } - .label { - font-size: 12px; - color: var(--secondary-text-color, #9aa4ad); - white-space: nowrap; - } - .trigger { - width: 40px; - height: 40px; - flex: none; - box-sizing: border-box; - padding: 3px; - border: 1px solid var(--divider-color, #666); - border-radius: 7px; - background: - linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, - linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; - cursor: pointer; - } - :host([data-pointer-hover]) .trigger:hover { - border-color: var(--primary-color, #03a9f4); - } - .trigger[aria-expanded='true'] { - border-color: var(--primary-color, #03a9f4); - outline: none; - box-shadow: 0 0 0 1px var(--primary-color, #03a9f4); - } - .trigger:focus-visible { - border-color: var(--primary-color, #03a9f4); - outline: 2px solid var(--primary-color, #03a9f4); - outline-offset: 2px; - } - .swatch { - display: block; - width: 100%; - height: 100%; - border-radius: 3px; - box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.18); - pointer-events: none; - } - .picker { - /* The popover attribute promotes this surface into the browser top layer, outside - hp-dialog's scrolling/clipping body. JS supplies viewport coordinates. */ - position: fixed; - z-index: 2147483647; - inset: auto; - top: 0; - left: 0; - width: min(292px, calc(100vw - 16px)); - max-height: calc(100vh - 16px); - margin: 0; - box-sizing: border-box; - display: grid; - gap: 10px; - padding: 12px; - overflow: auto; - color: var(--primary-text-color, #fff); - background: var(--card-background-color, #202126); - border: 1px solid var(--primary-color, #03a9f4); - border-radius: 10px; - box-shadow: 0 10px 28px rgb(0 0 0 / 0.34); - } - .picker[popover]:not(:popover-open) { - /* Keep the author-level display:grid from exposing the surface for one - frame before showPopover() promotes it into the top layer. */ - display: none; - } - .picker-head, - .row, - .control-head { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; - } - .picker-head { - justify-content: space-between; - } - .caption { - flex: 1; - min-width: 0; - overflow: hidden; - color: var(--secondary-text-color, #9aa4ad); - font-size: 12px; - text-overflow: ellipsis; - white-space: nowrap; - } - .preview { - width: 52px; - height: 30px; - flex: none; - box-sizing: border-box; - padding: 3px; - border: 1px solid var(--divider-color, #666); - border-radius: 7px; - background: - linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, - linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; - } - .preview > span { - display: block; - width: 100%; - height: 100%; - border-radius: 3px; - } - .sv-field { - position: relative; - width: 100%; - height: 132px; - overflow: hidden; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 8px; - background: - linear-gradient(to top, #000, transparent), - linear-gradient(to right, #fff, var(--hp-picker-hue, #f00)); - cursor: crosshair; - touch-action: none; - user-select: none; - } - .sv-thumb { - position: absolute; - left: var(--hp-picker-saturation, 0%); - top: var(--hp-picker-value, 100%); - width: 14px; - height: 14px; - box-sizing: border-box; - border: 2px solid #fff; - border-radius: 50%; - box-shadow: 0 0 0 1px rgb(0 0 0 / .65), 0 1px 3px rgb(0 0 0 / .5); - transform: translate(-50%, -50%); - pointer-events: none; - } - .control { - display: grid; - gap: 2px; - min-width: 0; - } - .control-value { - flex: none; - color: var(--secondary-text-color, #9aa4ad); - font-size: 12px; - } - input[type='range'] { - width: auto; - height: 40px; - min-width: 0; - flex: 1; - margin: -5px 0; - accent-color: var(--primary-color, #03a9f4); - touch-action: none; - } - .hue-range { - accent-color: var(--hp-picker-hue, #f00); - --hp-picker-hue-track: linear-gradient(to right, - #f00 0%, #ff0 16.667%, #0f0 33.333%, #0ff 50%, - #00f 66.667%, #f0f 83.333%, #f00 100%); - --hp-picker-hue-thumb-shadow: - 0 0 0 2px var(--card-background-color, #202126), - 0 0 0 3px var(--primary-text-color, #fff); - } - .hue-range::-webkit-slider-runnable-track { - height: 10px; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 999px; - background: var(--hp-picker-hue-track); - } - .hue-range::-moz-range-track { - height: 10px; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 999px; - background: var(--hp-picker-hue-track); - } - .hue-range::-moz-range-progress { - height: 10px; - border: 0; - border-radius: 999px; - background: transparent; - } - .hue-range::-webkit-slider-thumb { - border-radius: 50%; - box-shadow: var(--hp-picker-hue-thumb-shadow); - } - .hue-range::-moz-range-thumb { - border-radius: 50%; - box-shadow: var(--hp-picker-hue-thumb-shadow); - } - input[type='text'], - input[type='number'] { - min-height: 40px; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 6px; - padding: 6px 8px; - color: var(--primary-text-color, #fff); - background: var(--input-fill-color, transparent); - font: inherit; - } - input[type='text'] { - width: 100%; - } - input[type='number'] { - width: 50px; - flex: none; - } - input:focus-visible, - .sv-field:focus-visible { - outline: 2px solid var(--primary-color, #03a9f4); - outline-offset: 1px; - } - input[aria-invalid='true'] { - border-color: var(--error-color, #db4437); - } - .error { - color: var(--error-color, #db4437); - font-size: 11px; - line-height: 1.25; - } - .pct { - color: var(--secondary-text-color, #9aa4ad); - font-size: 12px; - } - :host([disabled]) { - opacity: .5; - pointer-events: none; - } - @media (prefers-reduced-motion: reduce) { - .picker { transition: none !important; } - } - @media (forced-colors: active) { - .hue-range::-webkit-slider-runnable-track { - border-color: ButtonText; - background: Canvas; - } - .hue-range::-moz-range-track { - border-color: ButtonText; - background: Canvas; - } - .hue-range::-webkit-slider-thumb { - box-shadow: none; - } - .hue-range::-moz-range-thumb { - box-shadow: none; - } - } - `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",Ao);const Fo=new WeakMap;function No(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=Fo.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Fo.set(e,s)),s}function Ho(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Lo(t,e){const s=No(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Ho(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Ho(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,Ho(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function Eo(t){const e=No(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function Oo(t,e,s=Eo(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function Bo(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:Oo(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function Wo(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function jo(t,e,s,i=Eo(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return Bo(t,o.platform||n,"entity-platform",i,a)}class Go extends f{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=Lo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return l(this.hass)}_t(t,e){return y(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=Eo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=jo(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return Bo(t,s?.domain,"registry-owner",i,e)});if(l.some(Boolean))return Wo(l);const h=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&h.push(jo(t,e,s,i));return h.some(Boolean)?Wo(h):Wo((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return Bo(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,Eo(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=jo(this.hass,t,this.registry,Eo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return d;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=$(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),o="none"!==t.pulse.kind,a=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let n=-a/2,r=a/2,l=-a/2,h=a/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(r=Math.max(r,i+t)),"left"===s.valueBadge.position&&(n=Math.min(n,-i-t)),"top"===s.valueBadge.position&&(l=Math.min(l,-i-e)),"bottom"===s.valueBadge.position&&(h=Math.max(h,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);h=Math.max(h,t)}const c=Math.min(1,2.35/Math.max(1,r-n),2.35/Math.max(1,h-l)),u=Math.round(100*c),_=[`left:calc(50% - ${54*((n+r)/2)*c}px)`,`top:calc(50% - ${54*((l+h)/2)*c}px)`,c<1?`transform:scale(${c})`:""].filter(Boolean).join(";"),m=["dev",w(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),g=k(s).join(";"),f=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),b=[this.deviceName,this._providerSummary(i),this._stateSummary(t),f,t.valueFullText||"",x(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return p`
    -
    - ${this._t("marker.preview.title")} - - ${this._t(e?"marker.preview.example":"marker.preview.actual")} - -
    -
    - -
    -
    ${this._t("marker.preview.integration")}${this._providerSummary(i)}
    -
    ${this._t("marker.preview.source")}${this._sourceSummary(t)}
    -
    ${this._t("marker.preview.current_state")}${this._stateSummary(t)}
    -
    ${this._t("marker.preview.result")}${f}
    - ${t.fallbackReason?p`

    ${this._reason(t.fallbackReason)}

    `:d} - ${t.explanation.notices.map(t=>p`

    ${this._reason(t)}

    `)} - ${c<1?p`

    ${this._t("marker.preview.scaled",{n:u})}

    `:d} - ${e&&this._reducedMotion?p`

    ${this._t("marker.preview.reduced_motion")}

    `:d} -
    -
    - ${t.visualSources.length||t.criticalSources.length?p`
    - ${this._t("marker.preview.details")} -
    - ${[...t.visualSources,...t.criticalSources].map(t=>p` -
    - ${t.name}${t.eid} - ${t.stateText||t.state} - ${this._sourceProvider(t.eid)} -
    `)} -
    -
    `:d} - ${"icon_ripple"===t.display?p`
    - - -
    `:d} -
    `}}Go.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},Go.styles=[v,g` - :host { display: block; min-width: 0; } - .devicepreview { - margin: 4px 0 14px; - padding: 14px; - border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.28)); - border-radius: 14px; - background: color-mix(in srgb, var(--card-background-color, #20232b) 92%, var(--primary-color, #03a9f4)); - color: var(--primary-text-color, #fff); - overflow: hidden; - } - .previewhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; } - .previewbadge { - padding: 3px 9px; - border-radius: 999px; - color: var(--secondary-text-color, #9aa0aa); - background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 12%, transparent); - font-size: 12px; - font-weight: 700; - } - .previewbadge.example { color: var(--warning-color, #ffb300); } - .previewgrid { display: grid; grid-template-columns: minmax(170px, 0.8fr) minmax(240px, 1.2fr); gap: 16px; align-items: stretch; } - .previewstage { - position: relative; - min-height: 168px; - border-radius: 12px; - background: var(--secondary-background-color, rgba(0, 0, 0, 0.16)); - overflow: hidden; - pointer-events: none; - container-type: inline-size; - --icon-size: 54px; - --device-base-size: 48.6px; - } - .previewfit { position: absolute; left: 50%; top: 50%; transform-origin: center; } - .previewstage .dev { left: 0; top: 0; cursor: default; } - :host([data-pointer-hover]) .previewstage .dev:hover { z-index: 2; } - .previewfacts { min-width: 0; display: grid; align-content: start; gap: 8px; } - .previewfacts > div { display: grid; grid-template-columns: minmax(115px, 0.8fr) minmax(0, 1.3fr); gap: 8px; } - .previewfacts span { color: var(--secondary-text-color, #9aa0aa); } - .previewfacts b { min-width: 0; overflow-wrap: anywhere; } - .previewnotice { margin: 2px 0 0; color: var(--secondary-text-color, #9aa0aa); font-size: 13px; } - .previewdetails { margin-top: 12px; } - .previewdetails summary { cursor: pointer; color: var(--secondary-text-color, #9aa0aa); } - .previewtable { display: grid; gap: 8px; margin-top: 9px; } - .previewsource { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3px 10px; font-size: 13px; } - .previewsource > * { min-width: 0; overflow-wrap: anywhere; } - .previewsource code { color: var(--secondary-text-color, #9aa0aa); } - .previewdemo { - min-height: 40px; - display: inline-flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.35)); - border-radius: 10px; - background: transparent; - color: inherit; - cursor: pointer; - } - .previewdemos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } - .previewdemo:focus-visible { outline: 2px solid var(--primary-color, #03a9f4); outline-offset: 2px; } - .previewdemo:disabled { opacity: 0.5; cursor: not-allowed; } - @media (max-width: 640px) { - .previewgrid { grid-template-columns: minmax(0, 1fr); } - .previewstage { min-height: 142px; } - .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } - } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",Go);const qo={revision:0,topologies:[],states:{}};class Uo extends f{constructor(){super(...arguments),this.value={enabled:!1,z2mBaseTopics:[]},this.savedEnabled=!1,this.devices=[],this._runtime=null,this._snapshot=qo,this._topicText="zigbee2mqtt",this._invalidTopic=!1}disconnectedCallback(){this._release?.(),this._release=void 0,super.disconnectedCallback()}updated(t){t.has("hass")&&(this._release?.(),this._release=void 0,this._runtime=null,this._snapshot=qo),this.savedEnabled&&this._admin&&this._ensureRuntime()}willUpdate(t){t.has("value")&&(this._topicText=(this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]).join("\n"),this._invalidTopic=!1)}get _admin(){return!0===this.hass?.user?.is_admin}_t(t,e){return po(l(this.hass),t,e)}async _ensureRuntime(){return this._runtime||(this._runtime=await import("./zigbee-topology-runtime-BFjOXm55.js"),this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this._release=this._runtime.subscribeZigbeeTopology(this.hass,()=>{this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this.requestUpdate()}),this.requestUpdate()),this._runtime}_emit(t){this._admin&&this.dispatchEvent(new CustomEvent("hp-topology-settings-change",{detail:t,bubbles:!0,composed:!0}))}_topics(){return this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]}_editTopics(t){this._topicText=t;const e=t.split(/[\r\n,]+/).map(t=>t.trim()).filter(Boolean),s=e.map(M).filter(Boolean);this._invalidTopic=s.length!==e.length,this._invalidTopic?this.requestUpdate():this._emit({...this.value,z2mBaseTopics:[...new Set(s)].slice(0,8)})}_status(t){const e=this._snapshot.states[t];if(!e)return this._t("status_idle");if("loading"===e.phase)return this._t("status_loading");if("error"===e.phase)return this._t(`error_${e.error||"provider"}`);if("ready"!==e.phase||!e.obtainedAt)return this._t("status_idle");const s=new Date(e.obtainedAt).toLocaleTimeString(l(this.hass),{hour:"2-digit",minute:"2-digit"}),i=this._snapshot.topologies.find(e=>"zha"===t?"zha"===e.provider:`z2m:${e.instanceId}`===t),o=!!i&&!!this.registry&&uo(i,this.devices,this.registry).warnings.length>0;return e.partial||o?this._t("status_partial",{time:s}):i&&!i.links.length?this._t("status_no_links",{time:s}):this._t(Date.now()-e.obtainedAt>_o?"status_stale":"status_ready",{time:s})}_busy(t){return"loading"===this._snapshot.states[t]?.phase}async _readZha(){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).readZhaTopology(this.hass)}async _refreshZ2m(t){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).refreshZ2mTopology(this.hass,t)}render(){const t=this._admin,e=this.value.enabled,s=t&&this.savedEnabled,i=this._topics();return p` -
    ${this._t("title")}
    - -
    ${this._t("hint")}
    - ${t?d:p`
    ${this._t("admin_only")}
    `} - ${e?p`
    - ${this.savedEnabled?d:p`
    ${this._t("save_first")}
    `} -
    -
    ${this._t("zha")}
    -
    ${this._t("zha_hint")}
    -
    - - ${this._status("zha")} -
    -
    -
    -
    ${this._t("z2m")}
    - - - ${this._invalidTopic?p`
    ${this._t("error_invalid_topic")}
    `:d} -
    ${this._t("z2m_warning")}
    - ${i.map(t=>p`
    - - ${this._status(`z2m:${t}`)} -
    `)} -
    -
    `:d} - `}}Uo.properties={hass:{attribute:!1},value:{attribute:!1},savedEnabled:{type:Boolean,attribute:"saved-enabled"},devices:{attribute:!1},registry:{attribute:!1}},Uo.styles=g` - :host { display: block; color: inherit; font: inherit; } - .section { margin-top: 18px; font-weight: 700; } - .toggle { display: flex; align-items: center; gap: 10px; margin-top: 10px; } - .hint, .status { color: var(--secondary-text-color, #9aa0aa); font-size: 12px; line-height: 1.45; } - .hint { margin-top: 6px; } - .providers { display: grid; gap: 14px; margin-top: 12px; padding-left: 34px; } - .provider { display: grid; gap: 7px; } - .provider-title { font-weight: 650; } - .actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } - textarea { - box-sizing: border-box; width: 100%; min-height: 58px; resize: vertical; - border: 1px solid var(--divider-color, #666); border-radius: 8px; padding: 8px; - color: var(--primary-text-color, inherit); background: var(--card-background-color, #202126); - font: inherit; - } - button { - display: inline-flex; align-items: center; gap: 6px; min-height: 36px; - border: 1px solid var(--divider-color, #666); border-radius: 9px; padding: 7px 11px; - color: inherit; background: transparent; cursor: pointer; font: inherit; font-weight: 600; - } - button:disabled, textarea:disabled { opacity: .5; cursor: default; } - .warning { color: var(--warning-color, #d89300); font-size: 12px; line-height: 1.45; } - `,customElements.get("hp-zigbee-topology-settings")||customElements.define("hp-zigbee-topology-settings",Uo);const Ko=(t,e)=>[t[0]-e[0],t[1]-e[1]],Vo=(t,e)=>[t[0]+e[0],t[1]+e[1]],Jo=(t,e)=>t[0]*e[0]+t[1]*e[1],Yo=t=>Math.hypot(t[0],t[1]);function Zo(t){let e=0;for(let s=0;st[1]!=l>t[1]&&t[0]<(r-a)*(t[1]-n)/(l-n)+a&&(s=!s)}return s}([r[0]+n[0]*l,r[1]+n[1]*l],t)&&(n=[-n[0],-n[1]]),n}function ta(t){const e=t.length;if(e<3)return!1;for(let s=0;s(t[0]-o[0])*r[1]-(t[1]-o[1])*r[0],h=t=>(t[0]-o[0])*r[0]+(t[1]-o[1])*r[1],c=s,d=n-s;for(const e of t){const t=Math.abs(l(e));if(t<=s)continue;const o=h(e);o<=c||o>=d||tMath.abs(n+(t-p)/g*(r-n));i=Math.min(i,f(_),f(m))}}return i}function sa(t,e,s){if(I(t,e,s))return!0;if(P(t,e,s)||P(e,t,s))return!1;let i=0;try{const s=T([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of s)t?.[0]&&(i+=R(t[0]))}catch{return!1}return i>Math.max(1e-7,s*s)}function ia(t,e,s){for(const i of e)for(let e=0;es)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-s&&h+t.length/2<=r+s)return!0}return!1}function oa(t,e,s){const i=Math.abs(e[0]-t[0]),o=Math.abs(e[1]-t[1]);return i<=s&&o>s?"v":o<=s&&i>s?"h":null}function aa(t,e,s){return Math.abs(t[0]-e[0])<=s&&Math.abs(t[1]-e[1])<=s}function na(t,e,s,i,o){return aa(t,s,o)&&aa(e,i,o)||aa(t,i,o)&&aa(e,s,o)}function ra(t,e,s,i,o){const a=oa(t,e,o);return a&&oa(s,i,o)===a?"h"===a?Math.abs(t[1]-s[1])>o||Math.abs(t[1]-i[1])>o?0:Math.max(0,Math.min(Math.max(t[0],e[0]),Math.max(s[0],i[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s[0],i[0]))):Math.abs(t[0]-s[0])>o||Math.abs(t[0]-i[0])>o?0:Math.max(0,Math.min(Math.max(t[1],e[1]),Math.max(s[1],i[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s[1],i[1]))):0}function la(t,e,s,i){const o=t.length,a=oa(t[(e-1+o)%o],t[e],i),n=oa(t[(e+1)%o],t[(e+2)%o],i),r="h"===s?"v":"h";return a===r&&n===r}function ha(t){let e=1/0,s=1/0,i=-1/0,o=-1/0;for(const a of t)e=Math.min(e,a[0]),s=Math.min(s,a[1]),i=Math.max(i,a[0]),o=Math.max(o,a[1]);return[e,s,i,o]}function ca(t,e,s){const i=ha(t),o=ha(e);return i[2]o}const pa=(t,e)=>"h"===e?t[0]:t[1],ua=(t,e)=>"h"===e?t[1]:t[0];function _a(t,e,s,i,o){const a=t.find(t=>t.id===e);if(!a)return null;const n=a.poly[s],r=a.poly[(s+1)%a.poly.length],l=oa(n,r,o);if(!l)return null;const h=0===i?r:n,c=0===i?n:r,d=pa(h,l),p=pa(c,l),u=ua(h,l),_=function(t,e,s,i){const o=[];for(const a of t)for(let t=0;ti||Math.abs(ua(r,e)-s)>i)continue;const l=pa(n,e),h=pa(r,e),c=Math.min(l,h),d=Math.max(l,h);d-c>i&&o.push({roomId:a.id,edge:t,lo:c,hi:d})}return o.sort((t,e)=>t.lo-e.lo||t.hi-e.hi||t.roomId.localeCompare(e.roomId)||t.edge-e.edge)}(t,l,u,o),m=function(t,e,s,i){const o=[e,s];for(const a of t){const t=Math.max(e,a.lo),n=Math.min(s,a.hi);n-t>i&&o.push(t,n)}const a=[...new Set(o)].sort((t,e)=>t-e),n=[];for(let e=0;e+1r>t.lo-i&&rt.roomId))].sort();n.push({lo:s,hi:o,owners:l})}return n}(_,Math.min(d,p),Math.max(d,p),o);return!m.length||m.some(t=>t.owners.length<1||t.owners.length>2)?null:{roomId:e,edge:s,movedEndpoint:i,axis:l,line:u,fixed:d,moving:p,intervals:_,baseline:m}}function ma(t,e,s){const i=[];for(const o of t.intervals){const a=e.polys[o.roomId];if(!a){i.push(o);continue}if(o.edge<0||o.edge>=a.length)continue;const n=a[o.edge],r=a[(o.edge+1)%a.length];if(oa(n,r,s)!==t.axis||Math.abs(ua(n,t.axis)-t.line)>s||Math.abs(ua(r,t.axis)-t.line)>s)continue;const l=pa(n,t.axis),h=pa(r,t.axis),c=Math.min(l,h),d=Math.max(l,h);d-c>s&&i.push({...o,lo:c,hi:d})}return i}function ga(t,e,s){return new Set(t.filter(t=>e>t.lo-s&&et.roomId)).size}function fa(t,e,s){for(const i of e.sideOwnership){const e=t.polys[i.roomId];if(!e)return!1;const o=e[i.edge],a=e[(i.edge+1)%e.length];if(oa(o,a,s)!==i.axis)return!1;const n=pa(0===i.movedEndpoint?o:a,i.axis),r=ma(i,t,s),l=(i.moving>=i.fixed?i.baseline[i.baseline.length-1]:i.baseline[0]).owners.length;if(l<1||l>2)return!1;const h=Math.min(i.fixed,i.moving,n),c=Math.max(i.fixed,i.moving,n),d=[h,c,i.fixed,i.moving,n];for(const t of i.intervals)d.push(t.lo,t.hi);for(const t of r)d.push(t.lo,t.hi);const p=[...new Set(d.filter(t=>t>=h&&t<=c))].sort((t,e)=>t-e),u=Math.min(i.fixed,i.moving),_=Math.max(i.fixed,i.moving),m=Math.min(i.fixed,n),g=Math.max(i.fixed,n);for(let t=0;t+1u-s&&a<_+s,h=a>m-s&&a2||d>2)return!1;if(n&&h){if(c!==d)return!1}else if(!n&&h){if(d!==l||c>0&&c!==d)return!1}else if(2===c?2!==d:d>1)return!1}}return!0}function ba(t,e,s,i){const o=[s.n[0]*i,s.n[1]*i],a={polys:{},openings:{},movedSpans:{}};for(const e of s.roomIds){const i=t.find(t=>t.id===e),n=s.edgeByRoom[e];if(!i||null==n)continue;const r=(n+1)%i.poly.length;a.polys[e]=i.poly.map((t,e)=>e===n||e===r?Vo(t,o):[...t]),a.movedSpans[e]=[[Vo(i.poly[n],o),Vo(i.poly[r],o)]]}for(const t of e)s.movingOpeningIds.includes(t.id)&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function va(t,e,s,i,o,a,n,r){if(!function(t,e,s,i){if(Xo([t.x,t.y],e,s)>2*i)return!1;const o=Yo(Ko(s,e));if(o<=i)return!1;const a=[(s[0]-e[0])/o,(s[1]-e[1])/o],n=(t.x-e[0])*a[0]+(t.y-e[1])*a[1];return n>=-i&&n<=o+i}(t,e,s,r))return!0;if(Xo([t.x,t.y],i,o)>2*r)return!1;const l=0===a?i:o;return Yo(Ko([t.x,t.y],l))>=t.length/2+n-r}function ya(t,e,s,i){const o=i.movingHalf||0;return"circle"===t.kind?Xo(t.center,e,s)2)return!1;const r=ba(t,e,s,i);if(Object.keys(r.polys).length!==s.roomIds.length)return!1;if(!fa(r,s,a))return!1;const l=new Set(s.roomIds),h=t=>r.polys[t.id]||t.poly;for(const i of s.roomIds){const l=t.find(t=>t.id===i),h=r.polys[i],c=s.edgeByRoom[i];if(!l||!h||h.length!==s.topology[i]||h.length!==l.poly.length||!ta(h))return!1;const d=l.poly.length,p=(c-1+d)%d,u=[p,c,(c+1)%d];for(const t of u)if(C(h[t],h[(t+1)%d]))return!1;const _=Zo(l.poly),m=Zo(h);if(Math.abs(m)t.id===e),i=r.polys[e];for(const o of t){if(o.id===e||l.has(o.id))continue;const t=h(o);if(!ca(s.poly,o.poly,a)||!ca(i,t,a))if(P(s.poly,o.poly,a)){if(!P(i,t,a))return!1}else if(P(o.poly,s.poly,a)){if(!P(t,i,a))return!1}else if(P(i,t,a)||P(t,i,a)||sa(i,t,a))return!1}}t.find(t=>t.id===s.roomId);const c=r.polys[s.roomId],d=s.edgeByRoom[s.roomId],p=c[d],u=c[(d+1)%c.length];if(Math.abs(i)>a)for(const t of o.obstacles||[])if(ya(t,p,u,o))return!1;for(const t of e){if(!s.movingOpeningIds.includes(t.id))continue;const e=r.openings[t.id];if(!e||t.hosted)return!1;if(!ia({...t,x:e[0],y:e[1]},[c],2*a))return!1}return!0}const wa=new WeakMap,ka=t=>"number"==typeof t&&Number.isFinite(t),xa=(t,e)=>{const s=new Map;for(const i of t||[])!ka(i?.cm)||!e&&i.cm<=0||s.set(i.cm,(s.get(i.cm)||0)+1);return s},Sa=t=>[String(t),"см"].join(" ");function Ma(t,e,s={}){if(s.allowClear)return[];const i=!0===s.exactMultiplicity,o=xa(t,i),a=xa(e,i),n=[];for(const[t,e]of o){const s=a.get(t)||0;0===s?n.push({invariant:"wall_records",kind:"lost",owner:Sa(t),reference:`было ${e}`,detail:"записи этой толщины исчезли целиком"}):i&&s!==e&&n.push({invariant:"wall_records",kind:"count",owner:Sa(t),reference:`было ${e}, стало ${s}`,detail:"изменилось число записей этой толщины"})}if(i)for(const[t,e]of a)o.has(t)||n.push({invariant:"wall_records",kind:"count",owner:Sa(t),reference:`было 0, стало ${e}`,detail:"появились записи новой толщины"});return n}class Da{constructor(){this._selectedRoomId=null,this._session=null,this._eligibility=null}get dragging(){return null!==this._session}ownsPointer(t){return this._session?.pointerId===t}get selectedRoomId(){return this._selectedRoomId}get snapshotIdentity(){return this._session?.snapshotIdentity||null}get activePointerId(){return this._session?.pointerId??null}get moved(){return!0===this._session?.moved}get delta(){return this._session?.delta||0}get rooms(){return this._session?.rooms||null}get openings(){return this._session?.openings||null}get plan(){return this._session?.plan||null}get preview(){return this._session?.accepted?.preview||null}get liveLabels(){return this._session?.accepted?.labels||null}selectRoom(t){this._selectedRoomId=t}restoreSelection(t){this._selectedRoomId=t}escapeIdle(){return this._selectedRoomId?(this._selectedRoomId=null,"selection-cleared"):"exit-tool"}resolve(t,e,s){this._eligibility&&this._eligibility.context===t||(this._eligibility={context:t,values:new Map});const i=this._eligibility.values.get(e);if(i)return i;const o=s();return this._eligibility.values.set(e,o),o}begin(t){return!this._session&&(this._session={pointerId:t.pointerId,start:[...t.start],roomId:t.roomId,plan:t.plan,options:t.options,rooms:t.rooms,openings:t.openings,snapshotIdentity:t.snapshotIdentity,before:t.before,moved:!1,delta:0,changedRoomIds:[...t.plan.roomIds],rejectionNotified:!1,wallUnionBefore:t.wallUnionBefore,accepted:null},!0)}move(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=(i=e.start,o=t.point,a=e.plan.n,[i?.[0],i?.[1],o?.[0],o?.[1],a?.[0],a?.[1]].every(Number.isFinite)?(o[0]-i[0])*a[0]+(o[1]-i[1])*a[1]:0);var i,o,a;const n=t.snap([e.plan.a[0]+e.plan.n[0]*s,e.plan.a[1]+e.plan.n[1]*s]),r=(n[0]-e.plan.a[0])*e.plan.n[0]+(n[1]-e.plan.a[1])*e.plan.n[1],l=function(t,e,s,i,o,a){if(!Number.isFinite(i)||Math.abs(i)<1e-9)return 0;const n=Math.sign(i),r=Math.abs(i),l=Math.max(Math.abs(o),1e-6);let h=wa.get(s);h&&h.opts===a||(h={opts:a,values:new Map},wa.set(s,h));const c=h.values;let d=0;for(let i=Math.min(l,r),o=0;o<4096&&i<=r+1e-9;o++,i=Math.min(r,i+l)){const o=n*i,h=`${o.toFixed(9)}|${l.toFixed(9)}`;let p=c.get(h);if(void 0===p&&(p=$a(t,e,s,o,a),c.size<4096&&c.set(h,p)),!p)break;if(d=o,Math.abs(i-r)<=1e-9)break}return d}(e.rooms,e.openings,e.plan,r,t.step,e.options);if(e.moved&&l===e.delta)return{kind:"no-op"};const h=ba(e.rooms,e.openings,e.plan,l);let c;try{if(c=t.project(e.snapshotIdentity,h.polys,h.openings,e.changedRoomIds,e.rooms),!c.ok)return this._projectionRejected(e);if(Ma(c.value.beforeWalls,c.value.afterWalls,{exactMultiplicity:!0}).length)return this._projectionRejected(e)}catch{return this._projectionRejected(e)}const d=e.accepted,p={preview:c.value.preview,labels:null,artifact:c.value.artifact,beforeWalls:c.value.beforeWalls,afterWalls:c.value.afterWalls};e.accepted=p;try{t.publish(p.preview,p.artifact),p.labels=t.measure(h,e.plan)}catch{e.accepted=d;try{t.publish(d?.preview||null,d?.artifact||null)}catch{}return this._projectionRejected(e)}return e.moved=!0,e.delta=l,{kind:"accepted",preview:c.value.preview,labels:p.labels,artifact:c.value.artifact}}finish(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=e.accepted;let i;if(e.moved&&Math.abs(e.delta)>1e-9&&s)if(t.currentSnapshotIdentity!==e.snapshotIdentity)i={kind:"rejected",reason:"stale-snapshot"};else if($a(e.rooms,e.openings,e.plan,e.delta,e.options))if(Ma(s.beforeWalls,s.afterWalls,{exactMultiplicity:!0}).length)i={kind:"rejected",reason:"wall-records"};else{let o=!1;try{o=t.validatePreview(s.preview)}catch{o=!1}i=o?{kind:"commit",preview:s.preview,before:e.before}:{kind:"rejected",reason:"invalid-candidate"}}else i={kind:"rejected",reason:"invalid-topology"};else i={kind:"no-op"};return this._session=null,"commit"===i.kind&&(this._eligibility=null),i}cancel(t,e){const s=this._session;return!s||void 0!==e&&s.pointerId!==e?{kind:"no-op"}:(this._session=null,this._eligibility=null,{kind:"cancelled",restoreWallUnion:t===s.snapshotIdentity?s.wallUnionBefore:null})}reset(){this._selectedRoomId=null,this._session=null,this._eligibility=null}_projectionRejected(t){const e=!t.rejectionNotified;return t.rejectionNotified=!0,{kind:"rejected",notify:e}}}const Ca=(t,e)=>[(t[0]-e.x)/Math.max(1e-9,e.w)*e.stageWidth,(t[1]-e.y)/Math.max(1e-9,e.h)*e.stageHeight];function Pa(t){const{poly:e,edge:s,text:i,view:o}=t,a=e[s],n=e[(s+1)%e.length],r=[(a[0]+n[0])/2,(a[1]+n[1])/2],l=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return(t=>{let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(e,s),h=Math.hypot(n[0]-a[0],n[1]-a[1])||1,c=[(n[0]-a[0])/h,(n[1]-a[1])/h],d=Ca(r,o),p=Ca(t.gearCenter,o),u=Math.max(34,7.2*i.length+12),_=Math.abs(l[0])>=Math.abs(l[1])?Math.max(28,u/2+4):Math.max(28,13),m=d[0]+l[0]*_,g=d[1]+l[1]*_,f=e=>{return s=m+c[0]*e,i=g+c[1]*e,o=u,a=18,n=p[0],r=p[1],l=t.gearWidthPx,h=t.gearHeightPx,2*Math.abs(s-n)=Math.abs(c[1])?c[0]:c[1])>0?-1:1,e=Math.max(o.stageWidth,o.stageHeight,64);for(let s=4;s<=e;s+=4){const e=s*t;if(!f(e)){b=e;break}const i=-e;if(!f(i)){b=i;break}}}const v=l[0]*_+c[0]*b,y=l[1]*_+c[1]*b,$=Math.hypot(v,y)||1,w=y/$*12,k=[r[0]+v/$*12*o.w/Math.max(1,o.stageWidth),r[1]+w*o.h/Math.max(1,o.stageHeight)];return{anchor:r,offsetXPx:v,offsetYPx:y,tangentOffsetPx:b,side:Math.abs(l[0])>=Math.abs(l[1])?l[0]<0?"left":"right":l[1]<0?"above":"below",leader:{a:r,b:k}}}const Ia=t=>{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Ta(t,e,s,i,o,a=0,n=[t,e],r){const l=1e-7,h=(t,e,s,i,o)=>{const a=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*i)/o));return{t:a,x:e[0]+a*s,y:e[1]+a*i}},c=t=>{if(!t||!Number.isFinite(t[0])||!Number.isFinite(t[1]))return null;const e=Math.hypot(t[0],t[1]);return e>1e-9?[t[0]/e,t[1]/e]:null},d=c(r);let p=null;for(const r of i){const i=Number(r?.a?.[0]),u=Number(r?.a?.[1]),_=Number(r?.b?.[0]),m=Number(r?.b?.[1]);if(![i,u,_,m].every(Number.isFinite))continue;const g=_-i,f=m-u,b=g*g+f*f;if(!(b>1e-18))continue;const v=Math.sqrt(b),y=h(n,[i,u],g,f,b),$=Math.hypot(n[0]-y.x,n[1]-y.y);if(!($=2?r.axisA:r.a,e=r.axisB?.length>=2?r.axisB:r.b,s=Number(e[0])-Number(t[0]),i=Number(e[1])-Number(t[1]),o=s*s+i*i;if(o>1e-18&&[t[0],t[1],e[0],e[1]].every(Number.isFinite)){const e=h(n,t,s,i,o),a=(n[0]-e.x)*w[0]+(n[1]-e.y)*w[1];if(a>l)k=2;else if(a<-1e-7){if(r.roomSide)continue;k=-2}else d&&(k=w[0]*d[0]+w[1]*d[1])}}else{let t=n[0]-y.x,e=n[1]-y.y;const s=Math.hypot(t,e);if(s>l)t/=s,e/=s,k=2;else if(d)[t,e]=d,k=1;else{const s=i<_||i===_&&u<=m;t=-(s?f:-f)/v,e=(s?g:-g)/v}w=[t,e]}const x=h([t,e],[i,u],g,f,b);let S=x.t,M=x.x,D=x.y;if(Number.isFinite(a)&&a>0){const t=Math.round(S*v/a)*a;S=Math.max(0,Math.min(1,t/v)),M=i+S*g,D=u+S*f}const C=Ia(180*Math.atan2(-w[0],w[1])/Math.PI),P={cx:M+w[0]*(s/2),cy:D+w[1]*(s/2),angle:C,dist:$,sideScore:k,stableId:"string"==typeof r.stableId?r.stableId:""};(!p||P.distp.sideScore+l||Math.abs(P.sideScore-p.sideScore)<=l&&P.stableId.localeCompare(p.stableId)<0))&&(p=P)}return p&&{cx:p.cx,cy:p.cy,angle:p.angle,dist:p.dist}}const Ra=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),za=(t,e)=>{const s=t=>`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=s(t),o=s(e);return i<=o?`${i}|${o}`:`${o}|${i}`};function Aa(t,e,s,i,o,a,n=1){const r=[],l=new Map;for(const h of t||[]){const c="string"==typeof h?.id?h.id:"";if(!c)continue;const d=H(t,c,e,s,i,o,a,n);if(d)for(let t=0;t1e-9))continue;const a=L(d.poly,t),n=Number(d.offsets[t]),h=Number.isFinite(n)&&n>0?n:0,p=[e[0],e[1]],u=[s[0],s[1]],_=za(p,u),m=l.get(_)||new Set;m.add(c),l.set(_,m),r.push({a:[p[0]+a[0]*h,p[1]+a[1]*h],b:[u[0]+a[0]*h,u[1]+a[1]*h],axisA:p,axisB:u,normal:a,owner:"room",stableId:`room:${c}:${_}`,roomId:c,atomId:_,half:h})}}const h=[];for(const t of r){const{atomId:e,half:s,...i}=t;if(!(1===l.get(e)?.size&&s>1e-9)){h.push(i);continue}const o=i.normal;h.push({...i,roomSide:"inside",stableId:`${i.stableId}:inside`}),h.push({...i,a:[i.axisA[0]-o[0]*s,i.axisA[1]-o[1]*s],b:[i.axisB[0]-o[0]*s,i.axisB[1]-o[1]*s],normal:[-o[0],-o[1]],roomSide:"outside",stableId:`${i.stableId}:outside`})}return h}function Fa(t){const e=[],s=new Set;for(const i of t||[])if(Array.isArray(i)&&!(i.length<2))for(let t=0;t1e-9))continue;const n=[o[0],o[1]],r=[a[0],a[1]],l=`physical:${za(n,r)}`;s.has(l)||(s.add(l),e.push({a:n,b:r,axisA:[...n],axisB:[...r],normal:null,owner:"physical",stableId:l}))}return e}const Na=new WeakMap;const Ha=1e-9,La=1e7,Ea=t=>{const e=Math.sign(t)*Math.floor(Math.abs(t)*La+.5)/La;return Object.is(e,-0)?0:e},Oa=t=>`${Ea(t[0])},${Ea(t[1])}`,Ba=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),Wa=(t,e)=>Math.atan2(e[1]-t[1],e[0]-t[0]),ja=t=>Array.isArray(t)&&t.length>=2&&t.every(t=>Number.isFinite(t)),Ga=t=>(t||[]).filter(t=>ja(t?.a)&&ja(t?.b)&&Ba(t.a,t.b)>Ha);function qa(t,{minAngleDeg:e=15,maxValence:s=6}={}){const i=new Map;for(const e of Ga(t))for(const[t,s]of[[e.a,e.b],[e.b,e.a]]){const e=i.get(Oa(t))||[];e.push(Wa(t,s)),i.set(Oa(t),e)}const o=[];for(const[t,a]of i){if(a.length>s&&o.push({rule:"valence",subject:t,actual:a.length,limit:s}),a.length<2)continue;const i=[...a].sort((t,e)=>t-e);let n=1/0;for(let t=0;tHa&&s(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI%180+180)%180,Ka=(t,e,s=1)=>{const i=Math.abs(Ua(t)-Ua(e));return Math.min(i,180-i)<=s},Va=t=>{const e=new Map;for(const s of t)for(const t of[s.a,s.b]){const i=e.get(Oa(t));i?i.push(s):e.set(Oa(t),[s])}return e};function Ja(t,e,s){const i=Ga(e),o=s??Va(i),a=new Set([t]);let n=Ba(t.a,t.b);const r=[t.a,t.b];for(;r.length;){const e=r.pop();for(const s of o.get(Oa(e))||[])a.has(s)||Ka(s,t)&&Number(s.cm||0)===Number(t.cm||0)&&(a.add(s),n+=Ba(s.a,s.b),r.push(s.a,s.b))}return n}function Ya(t,e,s,{minLengthCm:i=20}={}){const o=[],a=Ga(t),n=Va(a);for(const t of a){const r=Ja(t,a,n)/s*(e||1),l=Math.max(i,Number(t.cm)>0?Number(t.cm):0);r{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a<=Ha?0:Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))};function Xa(t,e,s,{minDistanceCm:i=5}={}){const o=Ga(t),a=new Map;for(const t of o)a.set(Oa(t.a),t.a),a.set(Oa(t.b),t.b);const n=((t,e,s)=>t/(e||1)*s)(i,e,s),r=n>Ha?n:1,l=(t,e)=>`${Math.floor(t/r)},${Math.floor(e/r)}`,h=new Map;for(const[t,e]of a){const s=l(e[0],e[1]),i=h.get(s);i?i.push([t,e]):h.set(s,[[t,e]])}const c=new Map;for(const t of o){const e=Math.min(t.a[0],t.b[0])-n,s=Math.max(t.a[0],t.b[0])+n,i=Math.min(t.a[1],t.b[1])-n,o=Math.max(t.a[1],t.b[1])+n;for(let a=Math.floor(e/r);a<=Math.floor(s/r);a++)for(let e=Math.floor(i/r);e<=Math.floor(o/r);e++){const s=`${a},${e}`,i=c.get(s);i?i.push(t):c.set(s,[t])}}const d=[];for(const[t,o]of a){const a=Math.floor(o[0]/r),l=Math.floor(o[1]/r);for(let r=-1;r<=1;r++)for(let c=-1;c<=1;c++)for(const[p,u]of h.get(`${a+r},${l+c}`)||[]){if(t>=p)continue;const a=Ba(o,u);a<=2e-7||a{const i=a[(s+1)%a.length];return t+(e[0]*i[1]-i[0]*e[1])},0))/2,r=(s||1)/i,l=n*r*r;return lt.removed).map(t=>t.binding)),c=new Set;for(const t of s)t.id!==n&&("device"===t.bindingKind&&t.bindingRef&&c.add(`device:${t.bindingRef}`),"entity"===t.bindingKind&&t.bindingRef&&c.add(`entity:${t.bindingRef}`));const d=new Set;for(const t of s)"device"===t.bindingKind&&t.name&&d.add(`${t.name.trim()}|${t.area||""}`);const p=[];for(const t of Object.values(e?.devices||{})){if(!t||"service"===t.entry_type)continue;const e=`device:${t.id}`;if(c.has(e))continue;const s=String(t.name_by_user||t.name||t.id).trim();e!==a&&!h.has(e)&&d.has(`${s}|${t.area_id||""}`)||p.push({value:e,label:s,sub:(t.model||r.device)+("Group"===t.model?r.z2mGroup:""),kind:"device",ref:t.id,areaId:t.area_id||"",model:t.model||""})}for(const[t,s]of Object.entries(e?.entities||{})){const i=`entity:${t}`;if(c.has(i))continue;if(B(e,t,l)&&!h.has(i))continue;const o=tn.has(s?.platform),a="group"===s?.platform;if(!o&&!a)continue;if(s?.hidden&&!h.has(i))continue;const n=e?.states?.[t];p.push({value:i,label:s?.name||n?.attributes?.friendly_name||t,sub:`${t.split(".")[0]} · ${a?r.group:r.helper}`,kind:"entity",ref:t,areaId:s?.area_id||s?.device_id&&e?.devices?.[s.device_id]?.area_id||"",model:"",parentDeviceId:s?.device_id||void 0})}if(o){const t=new Set(p.map(t=>t.value));for(const[s,i]of Object.entries(e?.entities||{})){const o=`entity:${s}`;if(c.has(o)||t.has(o)||i?.hidden&&!h.has(o))continue;const a=!!i?.device_id&&l.devices.has(i.device_id);if(B(e,s,l)&&!h.has(o)&&!a)continue;const n=e?.states?.[s],d=i?.device_id?e?.devices?.[i.device_id]:null,u=d?String(d.name_by_user||d.name||""):"";p.push({value:o,label:i?.name||n?.attributes?.friendly_name||s,sub:`${s.split(".")[0]} · ${r.entity}${u?` · ${u}`:""}`,kind:"entity",ref:s,areaId:i?.area_id||d?.area_id||"",model:"",parentDeviceId:i?.device_id||void 0})}}return p.sort((t,e)=>t.label.localeCompare(e.label)||t.value.localeCompare(e.value))}const sn={kind:"active",enabledEntityIds:[],allEntityIds:[]};function on(t){return t.startsWith("entity:")?"entity":"device"}function an(t){const e=t.binding.indexOf(":"),s=e>=0?t.binding.slice(e+1):"",i=Object.keys(t).filter(t=>!["id","binding","hidden"].includes(t));return t.id===`h${s}`&&0===i.length}const nn=.001;function rn(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function ln(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function hn(t,e,s){return Math.abs(t[0]-e[0])"room"!==t.kind&&s.some(s=>s!==t&&function(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>s))return!1;const n=i/a,r=o/a,l=e=>Math.abs((e[0]-t.a[0])*r-(e[1]-t.a[1])*n);if(l(e.a)>s||l(e.b)>s)return!1;const h=(e.a[0]-t.a[0])*n+(e.a[1]-t.a[1])*r,c=(e.b[0]-t.a[0])*n+(e.b[1]-t.a[1])*r;return Math.min(a,Math.max(h,c))-Math.max(0,Math.min(h,c))>s}(t,s,e))),o=i.map(t=>{const[e,s]=cn(t.a,t.b);return{a:e,b:s,key:`hidden|${pn(t,e,s)}`,sourceKind:t.kind,sourceId:t.id}}).sort((t,e)=>t.key.localeCompare(e.key)),a=i.flatMap(t=>[t.a,t.b].map((e,s)=>({point:[e[0],e[1]],key:`hidden|${dn(t)}|endpoint-${s}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:o,endpoints:a}}function gn(t){const e=t.epsilon??nn,s=t.roomCuts||[],i=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!rn(e.a)||!rn(e.b))continue;const t=i.get(e.hostId)||[];t.push([e.a[0],e.a[1],e.b[0],e.b[1]]),i.set(e.hostId,t)}const o=[];for(const[e,i]of W(t.space.rooms).entries())i.length<4||o.push({a:[i[0],i[1]],b:[i[2],i[3]],kind:"room",id:`room-edge-${e}`,cuts:s});for(const e of t.space.room_drafts||[])if(e.id!==t.activeDraftId)for(let t=0;t+1_n(s,t,e)))continue;const t=ln(s);n.has(t)||n.set(t,{point:[s[0],s[1]],key:t})}}}return{segments:[...a.values()].sort((t,e)=>t.key.localeCompare(e.key)),endpoints:[...n.values()].sort((t,e)=>t.key.localeCompare(e.key))}}function fn(t,e,s){return e.some(e=>rn(e)&&hn(t,e,s))}function bn(t,e,s){if(!s)return!0;const i=t-s.distance;return i<-1e-9||Math.abs(i)<=1e-9&&e.localeCompare(s.key)<0}function vn(t,e,s){const i=s.epsilon??nn,o=s.excludePoints||[],a=[...t.endpoints,...(s.extraEndpoints||[]).filter(t=>rn(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],n=new Map;for(const t of a){if(fn(t.point,o,i))continue;const e=ln(t.point),s=n.get(e);(!s||t.key.localeCompare(s.key)<0)&&n.set(e,t)}return[...n.values()].map(t=>({kind:"endpoint",point:[...t.point],key:t.key,distance:Math.hypot(e[0]-t.point[0],e[1]-t.point[1])})).filter(t=>t.distance<=s.tolerance).sort((t,e)=>t.distance-e.distance||t.key.localeCompare(e.key))}function yn(t,e){if(!t.length)return null;if(e>0){const s=new Set;for(let i=0;i1)return{kind:"ambiguous",candidate:null,conflicts:t.filter(t=>s.has(t.key)).map(t=>({point:[...t.point],key:t.key}))}}return{kind:"resolved",candidate:t[0],conflicts:[]}}function $n(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>0))return[...t.a];const n=i/a,r=o/a,l=Math.max(0,Math.min(a,(e[0]-t.a[0])*n+(e[1]-t.a[1])*r)),h=s>0?Math.max(0,Math.min(a,Math.round(l/s)*s)):l;return[t.a[0]+n*h,t.a[1]+r*h]}function wn(t,e,s){if(!(rn(e)&&rn(s.anchor)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??nn,o=function(t,e){const s=e[0]-t[0],i=e[1]-t[1];if(!(Math.hypot(s,i)>Number.EPSILON))return null;const o=Math.round(Math.atan2(i,s)/(Math.PI/4))*(Math.PI/4);return[Math.cos(o),Math.sin(o)]}(s.anchor,e);if(!o)return{kind:"none",candidate:null,conflicts:[]};const a=vn(t,e,s).filter(t=>function(t,e,s,i){const o=t[0]-e[0],a=t[1]-e[1];return o*s[0]+a*s[1]>i&&Math.abs(o*s[1]-a*s[0])<=i*Math.max(Math.hypot(o,a),1)}(t.point,s.anchor,o,i)),n=yn(a,s.distinguishTolerance||0);if(n)return n;let r=null;for(const a of t.segments){const t=a.b[0]-a.a[0],n=a.b[1]-a.a[1],l=a.a[0]-s.anchor[0],h=a.a[1]-s.anchor[1],c=o[0]*n-o[1]*t;let d=null;if(Math.abs(c)<=i*Math.max(Math.hypot(t,n),1)){if(Math.abs(l*o[1]-h*o[0])>i*Math.max(Math.hypot(l,h),1))continue;const t=[a.a,a.b].map(t=>({point:t,along:(t[0]-s.anchor[0])*o[0]+(t[1]-s.anchor[1])*o[1]})).filter(t=>t.along>i).sort((t,e)=>t.along-e.along);t.length&&(d=[...t[0].point])}else{const e=(l*n-h*t)/c,a=(l*o[1]-h*o[0])/c;e>i&&a>=-i&&a<=1+i&&(d=[s.anchor[0]+o[0]*e,s.anchor[1]+o[1]*e])}if(!d)continue;const p=Math.hypot(e[0]-d[0],e[1]-d[1]);p>s.tolerance||fn(d,s.excludePoints||[],i)||!bn(p,a.key,r)||(r={kind:"line",point:d,key:a.key,distance:p,segment:a})}return r?{kind:"resolved",candidate:r,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}const kn=.001;function xn(t,e,s,i){const o=Number.isFinite(t)&&t>0?Math.floor(t):0,a=t=>"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,n=a(s),r=a(i)??0,l=[];let h=null;for(let t=0;t=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function Dn(t,e,s,i){return t*i-e*s}function Cn(t){let e=0;for(let s=0;st.localeCompare(e)),e[0]}function zn(t,e,s){const i=[...t];for(let t=!0;t&&i.length>=3;){t=!1;for(let o=0;o=0){i.splice(o,1),t=!0;break}}}return i}function An(t,e){const s=t.map(t=>Math.max(0,Math.min(1,t))).sort((t,e)=>t-e),i=[];for(const t of s)(!i.length||Math.abs(t-i[i.length-1])>e)&&i.push(t);return i}function Fn(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function Nn(t,e,s){const i=function(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o:0}(t,e);if(i<-s||i>1+s)return null;const o=Fn(e,i);return Math.hypot(o[0]-t[0],o[1]-t[1])<=s?Math.max(0,Math.min(1,i)):null}function Hn(t,e,s,i,o){const a=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=e.b[0]-e.a[0],l=e.b[1]-e.a[1],h=e.a[0]-t.a[0],c=e.a[1]-t.a[1],d=Dn(a,n,r,l),p=Math.max(Math.hypot(a,n),Math.hypot(r,l),1);if(Math.abs(d)>o*p){const t=Dn(h,c,r,l)/d,e=Dn(h,c,a,n)/d;return void(t>=-o&&t<=1+o&&e>=-o&&e<=1+o&&(s.push(Math.max(0,Math.min(1,t))),i.push(Math.max(0,Math.min(1,e)))))}if(!(Math.abs(Dn(h,c,a,n))>o*p)){for(const i of[e.a,e.b]){const e=Nn(i,t,o);null!=e&&s.push(e)}for(const s of[t.a,t.b]){const t=Nn(s,e,o);null!=t&&i.push(t)}}}function Ln(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function En(t,e){return t.minY-e.minY||t.index-e.index}function On(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function Bn(t){const e=t.right;return t.right=e.left,e.left=On(t),On(e)}function Wn(t){const e=t.left;return t.left=e.right,e.right=On(t),On(e)}function jn(t,e){return t?(En(e,t.item)<0?(t.left=jn(t.left,e),t.left.priority0)t.right=Gn(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority=e&&qn(t.left,e,s,i),t.item.minY<=s&&t.item.maxY>=e&&i.push(t.item),t.item.minY<=s&&qn(t.right,e,s,i))}function Un(t,e=.001){const s=Number.isFinite(e)&&e>0?e:kn,i=t.filter(t=>Mn(t.a)&&Mn(t.b)&&"string"==typeof t.key&&t.key.length>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>s),o=i.map(()=>[0,1]);!function(t,e,s){const i=t.map((t,e)=>({index:e,minX:Math.min(t.a[0],t.b[0])-s,maxX:Math.max(t.a[0],t.b[0])+s,minY:Math.min(t.a[1],t.b[1])-s,maxY:Math.max(t.a[1],t.b[1])+s})),o=[...i].sort((t,e)=>t.minX-e.minX||t.minY-e.minY||t.index-e.index),a=[...i].sort((t,e)=>t.maxX-e.maxX||t.index-e.index),n=new Set;let r=null,l=0;for(const i of o){for(;lt.index-e.index);for(const a of o)Hn(t[a.index],t[i.index],e[a.index],e[i.index],s);n.add(i.index),r=jn(r,i)}}(i,o,s);const a=new Map;for(let t=0;t({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function Kn(t,e=.001){const s=Number.isFinite(e)&&e>0?e:kn,i=Un(t,s),o=new Map,a=new Map,n=new Map(i.map(t=>[t.key,t]));for(const t of i){const e=In(t.a,s),i=In(t.b,s);o.set(e,t.a),o.set(i,t.b),a.has(e)||a.set(e,new Set),a.has(i)||a.set(i,new Set),a.get(e).add(i),a.get(i).add(e)}const r=new Map;for(const[t,e]of a){const s=o.get(t);r.set(t,[...e].sort((t,e)=>{const i=o.get(t),a=o.get(e);return Math.atan2(i[1]-s[1],i[0]-s[0])-Math.atan2(a[1]-s[1],a[0]-s[0])||t.localeCompare(e)}))}const l=new Set,h=new Map,c=(t,e)=>`${t}>${e}`;for(const t of i){const e=[In(t.a,s),In(t.b,s)];for(const[t,a]of[e,[e[1],e[0]]]){if(l.has(c(t,a)))continue;const e=[],d=[];let p=t,u=a,_=!1;for(let s=0;s<=2*i.length+2;s++){const s=c(p,u);if(l.has(s)){_=p===t&&u===a;break}l.add(s),e.push(p),d.push(Tn(p,u));const i=r.get(u)||[],o=i.indexOf(p);if(o<0||!i.length)break;if(p=u,u=i[(o-1+i.length)%i.length],p===t&&u===a){_=!0;break}}if(!_||new Set(e).size<3||new Set(e).size!==e.length)continue;const m=e.map(t=>o.get(t)),g=Cn(m);if(!(g>s*s))continue;const f=Rn(zn(e,o,s)),b=new Set;for(const t of d)for(const e of n.get(t)?.sourceKeys||[])b.add(e);const v={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...b].sort((t,e)=>t.localeCompare(e))};h.has(f)||h.set(f,v)}}return{atoms:i,faces:[...h.values()].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))}}function Vn(t,e,s=.001){if(!Mn(e))return null;const i=t.faces.filter(t=>{if(t.ring.some((i,o)=>function(t,e,s,i){const o=s[0]-e[0],a=s[1]-e[1],n=o*o+a*a;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1])<=i;const r=((t[0]-e[0])*o+(t[1]-e[1])*a)/n;return!(r<0||r>1)&&Math.hypot(e[0]+o*r-t[0],e[1]+a*r-t[1])<=i}(e,i,t.ring[(o+1)%t.ring.length],s)))return!1;let i=!1;for(let s=0,o=t.ring.length-1;se[1]!=n[1]>e[1]&&e[0]<(n[0]-a[0])*(e[1]-a[1])/(n[1]-a[1])+a[0]&&(i=!i)}return i});return[...i].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))[0]||null}function Jn(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(e[0]+i*n-t[0],e[1]+o*n-t[1])}function Yn(t,e,s=.5){let i=Math.abs(t-e)%180;return i>90&&(i=180-i),i<=s}function Zn(t,e,s,i){const o=t.filter(t=>"outer"===t.kind&&!t.open&&t.cm>0).map(t=>({interval:t,reusePartitionId:e.find(e=>function(t,e,s){return!(Math.abs(t.cm-e.cm)>1e-6)&&(Yn(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,.001)&&Jn(e.a,t.a,t.b)<=s&&Jn(e.b,t.a,t.b)<=s)}(e,t,i))?.id||null})),a=new Map;for(const e of s){if("partition"===e.host?.kind)continue;const s=t.map(t=>{const s=180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI;return{interval:t,distance:Yn(e.angle,s)?Jn([e.x,e.y],t.a,t.b):1/0}}).filter(t=>t.distance<=i).sort((t,e)=>t.distance-e.distance||t.interval.key.localeCompare(e.interval.key)),o=s[0]?.interval;"outer"===o?.kind&&!o.open&&o.cm>0&&a.set(e.id,o.key)}return{materialize:o,openingIntervals:a,removeOpeningIds:[...a.keys()].sort((t,e)=>t.localeCompare(e))}}function Xn(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?Math.max(0,Math.min(1,((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o)):0}const Qn=t=>!t.key.startsWith("static:room|");function tr(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;if(!(o>0))return null;const a=((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o,n=[e.a[0]+s*a,e.a[1]+i*a];return{point:n,t:a,distance:Math.hypot(n[0]-t[0],n[1]-t[1])}}function er(t,e){return e>0?Math.hypot(t[0]-Math.round(t[0]/e)*e,t[1]-Math.round(t[1]/e)*e):0}function sr(t,e,s){if(Qn(t.source)!==Qn(e.source))return Qn(t.source)?t:e;const i=er(t.point,s),o=er(e.point,s);return Math.abs(i-o)>1e-9?i>o?t:e:function(t,e){return t[0]-e[0]||t[1]-e[1]}(t.point,e.point)>0?t:e}function ir(t){return[t.from[0],t.from[1],t.to[0],t.to[1],t.targetKind].map(t=>"number"==typeof t?t.toFixed(6):t).join("|")}function or(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function ar(t,e){const s=e.epsilon??.001;if(!(e.maxDistance>=0)||!e.point&&!e.requiredSourceKey)return{kind:"none"};const i=new Map,o=t.flatMap(t=>[{source:t,endpoint:"a",point:t.a},{source:t,endpoint:"b",point:t.b}]);for(let t=0;ts)||l>e.maxDistance)continue;const h=sr(n,r,e.gridStep||0),c=h===n?r:n;if(!Qn(h.source))continue;const d={sourceKey:h.source.key,endpoint:h.endpoint,from:[h.point[0],h.point[1]],to:[c.point[0],c.point[1]],targetSourceKey:c.source.key,targetKind:"endpoint",distance:l};i.set(ir(d),d)}for(const a of o)if(Qn(a.source))for(const o of t){if(o===a.source)continue;const t=tr(a.point,o);if(!t||t.t<=s||t.t>=1-s||!(t.distance>s)||t.distance>e.maxDistance)continue;const n={sourceKey:a.source.key,endpoint:a.endpoint,from:[a.point[0],a.point[1]],to:t.point,targetSourceKey:o.key,targetKind:"line",distance:t.distance};i.set(ir(n),n)}const a=[];for(const o of i.values()){const i=Kn(or(t,o),s),n=e.point?Vn(i,e.point,s):i.faces.find(t=>!e.requiredSourceKey||t.sourceKeys.includes(e.requiredSourceKey))||null;n&&a.push({proposal:o,face:n})}if(a.sort((t,e)=>t.proposal.distance-e.proposal.distance||ir(t.proposal).localeCompare(ir(e.proposal))),!a.length)return{kind:"none"};return new Set(a.map(t=>`${ir(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:a.map(t=>t.proposal)}:{kind:"repair",...a[0]}}const nr=(t,e)=>(t.composedPath?.()||[t.target]).some(t=>(t=>!!t&&"function"==typeof t.matches)(t)&&e(t));class rr{constructor(t){this.host=t,this._openGroupId=null,this._groupGeneration=0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,this._globalDismissListening=!1,this._globalDismissGuard=t=>{this.handleOutsideDismiss(t)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(t){return this._openGroupId&&t.find(t=>t.id===this._openGroupId)||null}reset(){this._openGroupId=null,this._contentAnimation?.cancel(),this._contentAnimation=void 0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}closeForNavigation(){const t="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,e=!!this._openGroupId||!!t;this._openGroupId=null,t?.(),t&&(this._currentModel=null),this._clearDismissClick(),e&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(t,e){this._openGroupId!==e?t.some(t=>t.id===e)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=e,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(t){const e=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),t&&e&&this.host.updateComplete().then(()=>{const t=this.host.root(),s=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e&&null!==t.offsetParent);(s||t.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(t,e,s){const i=this._openGroupId===t.id,o=t.items.find(e=>e.id===t.activeItemId&&!e.disabled);return p``}renderGroupModel(t,e,s){const i=t.items.find(t=>!t.disabled)?.id,o=t.items.some(e=>e.id===t.activeItemId&&!e.disabled)?t.activeItemId:void 0;return{contextId:e,kind:"group",ariaLabel:s.openGroup(t.label),visibleLabel:t.label,content:p`
    this._groupKeydown(t)}> - ${t.items.map(e=>p``)} -
    `}}runContext(t,e,s){if(t===e)return s()}render(t,e){this._currentModel=t,this._blocked=e;const s=t?.kind||"hidden";return p`
    - -
    `}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(nr(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=nr(t,t=>t.classList.contains("editor-secondary")),i=nr(t,t=>!!t.dataset.editorGroup),o=nr(t,t=>!!t.dataset.editorPalette),a=nr(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const lr=t=>t.stopPropagation(),hr=1e-6*U,cr=6*U;function dr(t){return Number.isFinite(t)?Math.round(t/U)*U:t}const pr=(t,e,s,i)=>Math.hypot(s-t,i-e),ur=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),_r=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,l=Math.PI/180,h=Math.cos(s*l)*r,c=Math.sin(s*l)*r,d=Math.cos(n*l)*r,p=Math.sin(n*l)*r,u=Math.max(pr(t+h,e+c,o+d,a+p),pr(t-h,e-c,o-d,a-p)),_=Math.max(pr(t+h,e+c,o-d,a-p),pr(t-h,e-c,o+d,a+p));return Math.min(u,_)},mr=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function gr(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,l="",h=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=hr&&c++},u=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},_={};let m=5;for(const t of s){const e=mr(t);null!=t?.id&&(_[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>hr||i))return;o++,t>n&&(n=t);const a=t*K*e;a>r&&(r=a,l=s)};for(const t of s){const e=mr(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[dr(e[0]),dr(e[1])];return t=Math.max(t,pr(e[0],e[1],s[0],s[1])),u(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=dr(e+o),r=dr(s+a),l=dr(e),h=dr(s),c=Math.max(U,n-l),d=Math.max(U,r-h);t=ur(e,s,o,a,l,h,c,d),i.x=l,i.y=h,i.w=c,i.h=d,p(e,l),p(s,h),p(e+o,l+c),p(s+a,h+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[dr(e[0]),dr(e[1])];return t=Math.max(t,pr(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],l=n.length?[[o[0],n[0]]]:[],h=[];for(let t=0;t+1=2)for(const[t,e]of l)u(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[dr(o[0]),dr(o[1])],l=[dr(n[0]),dr(n[1])];let h=Math.max(pr(o[0],o[1],r[0],r[1]),pr(n[0],n[1],l[0],l[1]));const c=pr(r[0],r[1],l[0],l[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-hr&&i+e/2<=c+hr});c>hr&&d?(i.a=r,i.b=l,u(o,r),u(n,l)):h=0,g(h,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[dr(t[0]),dr(t[1])],n=pr(t[0],t[1],o[0],o[1]);i.center=o,u(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[dr(e[0]),dr(e[1])],a=[dr(s[0]),dr(s[1])];t=Math.max(pr(e[0],e[1],o[0],o[1]),pr(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],u(e,o),u(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=dr(e),r=dr(s);if(null!=i.w&&null!=i.h){const l=dr(e+o),h=dr(s+a),c=Math.max(U,l-n),d=Math.max(U,h-r);t=ur(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=pr(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=hr)continue;const l=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),u=!(Number.isFinite(p)&&p===d),_=_r(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,l,c,d);i.x=l,i.y=c,i.angle=d,u&&h++,g(_,e,s,u);continue}const o=q([i.x,i.y],t.rooms||[],cr,{step:U,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),l=_r(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&h++,g(l,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=dr(s.x),n=dr(s.y),r=pr(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const l="string"==typeof s.s?s.s:"";g(r,_[l]??m,l)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:l,rotated:h,removedDrafts:d},changed:o>0||c>0}}function fr(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const br=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),vr=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function yr(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return vr(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function $r(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&br(i.a)&&br(i.b)&&(vr(t,i.a)<=o||vr(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(vr(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function kr(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&br(t.a)&&br(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tvr(e,t.at)>s);let c=h[0]??r.a,d=h[h.length-1]??l.b;if(h.length<2&&(c=r.a,d=l.b),d[0]0?(d[0]-c[0])/u:0,m=u>0?(d[1]-c[1])/u:0,g=t=>u>0?((t[0]-c[0])*_+(t[1]-c[1])*m)/u:0;for(const t of[r,l]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function xr(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:fr(t.t,n)},a++;const r=J(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,Y(s,r,i.coordScale))}return a}function Sr(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>V(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const Mr=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Dr=(t,e)=>t[0]-e[0]||t[1]-e[1],Cr=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],Pr=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,Ir=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const l=a/r,h=n/r,c=e=>Math.abs((e[0]-t[0])*h-(e[1]-t[1])*l);if(c(s)>o||c(i)>o)return null;const d=Pr(s,t,l,h),p=Pr(i,t,l,h),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>o?{lo:u,hi:_}:null},Tr=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Rr=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},zr=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,l=a/n,h=nt(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!Mr(o))return!0;const a=Pr(o,t.a,r,l),c=Math.abs((o[0]-t.a[0])*l-(o[1]-t.a[1])*r),d=nt(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=h+d+i})},Ar=(t,e,s)=>{if(t.host){const i=J(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},Fr=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),l=Math.sin(n);if(Math.abs(o*l-a*r)>1e-6)return!1;const h=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(h*a-c*o)>s)return!1;const d=h*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Hr=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Lr=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Er(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],l=Array.isArray(t?.room_drafts)?t.room_drafts:[],h={walls:a,partitions:n,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!l.length)return h;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,u=r;const _=Z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const l=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(l>i)){e=!1;break}const h=s.flatMap(t=>{const e=t.cm>0?t.cm:X;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of h){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=l-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Rr(t))continue;const s=v.get(t.id);if(!(s&&Mr(s.a)&&Mr(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=Dr(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],l=n[1]-a[1],h=Math.hypot(r,l);if(!(h>c))continue;const _=r/h,g=l/h,y=Z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=Ir(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=u.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:J(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,h];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=Pr(e.center,a,_,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(h,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=h+c).map(t=>Math.max(0,Math.min(h,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>Nr(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=Cr(a,_,g,i),l=Cr(a,_,g,n),h=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of h)d.set(t.roomId,t);const p=[...d.values()],u=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:X)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===u.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:l,cm:s.cm},w=D.some(t=>!!Ir(r,l,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:X:0,I=v?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:l,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tTr(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&Q(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const l=tt(s,o.cellCm,o.gridPitch),h={lo:i.lo-l,hi:i.hi+l};for(const t of P)Tr(t,h,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>Tr(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=tt(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)Tr(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Lr(P,c),z=R.filter(t=>t.safe),A=R.filter(t=>!t.safe);if(!z.length)continue;if(p.length-1+A.length>2e3)continue;let F=d;for(const t of z){const[e,i]=Dr(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];F=et(F,e,i,t.finalCm,o.pitch,o.coordScale)}if(F=st(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),F.length>500)continue;const N=new Set(p.map(t=>t.id));N.delete(s.id);const H=A.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Hr(s.id,i,a,N);return N.add(n),{id:n,a:i,b:a,cm:s.cm}}),L=p.findIndex(t=>t.id===s.id),E=p.slice();E.splice(L,1,...H);const O=E.map(t=>Nr(t,o.coordScale)),B=new Map;let W=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=Y(t,s.resolved,o.coordScale),{host:i,...a}=e;B.set(t.id,a);continue}const i=S.get(t.id),a=H[A.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&O.find(t=>t.id===a.id);if(!a||!n){W=!1;break}const r=n.b[0]-n.a[0],l=n.b[1]-n.a[1],h=Math.hypot(r,l),d=Pr(e.center,n.a,r/h,l/h),p={...Y(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/h}};if(!it(p,O,o.coordScale,o.cellCm,o.gridPitch).resolved){W=!1;break}B.set(t.id,p)}if(!W)continue;const j=u.map(t=>B.get(t.id)||t),G=[...I.keys()].map(t=>B.get(t)),q=G.map(t=>Ar(t,O,o));if(q.some(t=>!t))continue;let U=!1;for(let t=0;tt.id));for(const t of j){if(K.has(t.id))continue;const e=Ar(t,O,o);e&&q.some(t=>Fr(t,e,c))&&(U=!0)}if(U)continue;const V=ot(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=B.get(t),i=at(V,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=F,p=E,u=j,f+=z.length,b+=I.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Or=t=>JSON.parse(JSON.stringify(t)),Br=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Wr=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function jr(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const Gr=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const qr=1e-9;class Ur extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const Kr=t=>JSON.parse(JSON.stringify(t)),Vr=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),Jr=t=>[Number(t[0]),Number(t[1])],Yr=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,Zr=(t,e)=>{const s=Yr(t),i=Yr(e);return sYr(t)<=Yr(e)?[Jr(t),Jr(e)]:[Jr(e),Jr(t)],Qr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),tl=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/U;Math.abs(i-Math.round(i))>=ut&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of V(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},sl=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},il=(t,e,s)=>{const i=Math.max(0,Math.min(1,sl(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},ol=(t,e,s,i,o=qr)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const l=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(l(s)>o||l(i)>o)return 0;const h=sl(s,t,e),c=sl(i,t,e);return Math.max(0,Math.min(1,Math.max(h,c))-Math.max(0,Math.min(h,c)))*r},al=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),l=new Uint32Array(64),h=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=h(s,17)^h(s,19)^s>>>10;l[t]=l[t-16]+i+l[t-7]+o>>>0}let[e,s,i,o,c,d,p,u]=r;for(let t=0;t<64;t++){const a=u+(h(c,6)^h(c,11)^h(c,25))+(c&d^~c&p)+n[t]+l[t]>>>0,r=e&s^e&i^s&i;u=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((h(e,2)^h(e,13)^h(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+u>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},rl=(t,e,s,i,o="")=>{const[a,n]=Xr(e,s),r=`${t}|${Yr(a)}|${Yr(n)}|${[...i].sort().join(",")}${o}`;return`wall-${nl(al(r)).slice(0,20)}`};let ll=0;const hl=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||nl(al(`${Date.now()}|${ll++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new Ur("duplicate-id","id factory exhausted")},cl=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&Vr(s.a)&&Vr(s.b)){if(e.has(s.id))throw new Ur("duplicate-id",s.id);e.set(s.id,s)}return e},dl=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=qr&&Math.abs(e[1]-i[1]-a)<=qr?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},pl=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=ct(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:dt(s,null,1,.04*U,!0),a=[];for(const t of s){const s=V(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>il([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*U)):[],l=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),h=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!_t(t,e,Number(s.angle)))return!1;if(il(i,t,e)>.02*U)return!1;const o=Qr(t,e),a=sl(i,t,e)*o,n=Number(s.length)/2;return a+n>=-qr&&a-n<=o+qr}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=V(i);if(!r||!p||p.length<3)throw new Ur("invalid-room",r);const u=pt(s,r,n,U,1,[...t.walls||[],...l]);if(!u||u.poly.length<3)throw new Ur("invalid-room",r);const _=Array.isArray(i.wall_ids)?i.wall_ids:[],m=_.length===p.length;let g=null,f=m;if(f)for(let t=0;tqr||Math.abs(i[1]-g[1])>qr)){f=!1;break}g=i}const b=[];for(let t=0;t2)throw new Ur("third-owner",n);const d=m?_[u.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||ol(s,i,o.a,o.b)>qr?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:Jr(p[u.parent[t]]),b:Jr(p[(u.parent[t]+1)%p.length])})}const g=u.parent[t];l.parentKeys.add(ht(p[g],p[(g+1)%p.length],U));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>il(v,[t[0],t[1]],[t[2],t[3]])<=.04*U);l.zeroWall||=y(a)||y(o)&&!h(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:u.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},ul=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[Zr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new Ur("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new Ur("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],l=i?s.get(i):void 0;if(l){r.set(t,l);continue}const h=i?t.preferredCarriers.get(i):void 0;if(i&&h){r.set(t,{id:i,a:h.a,b:h.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>ol(t.a,t.b,e.a,e.b)>qr),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||Qr(e.a,e.b)-Qr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const l=new Map;for(const[t,e]of r){const s=l.get(e.id)||[];s.push(t),l.set(e.id,s)}for(const[t,e]of l){const i=s.get(t)||r.get(e[0]);if(!i)throw new Ur("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=il(o,t.a,t.b)<=qr?0:1,a=il(o,e.a,e.b)<=qr?0:1;if(s!==a)return s-a;return(il(i.a,t.a,t.b)<=qr?0:1)-(il(i.a,e.a,e.b)<=qr?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const h=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(h.has(t.id))throw new Ur("duplicate-id",t.id);h.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=Xr(e.a,e.b),o=`${String(t.id||"")}|${Yr(s)}|${Yr(i)}|${[...e.owners].sort().join(",")}`,a=nl(al(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new Ur("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;h.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,h.add(o)}}else for(const t of c)t.id=hl(h),h.add(t.id)},_l=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=sl(s,e.a,e.b),o=Qr(e.a,e.b),a=Number(t.length)/2;return i>=-qr&&i<=1+qr&&il(s,e.a,e.b)<=.02*U&&_t(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-qr&&i*o+a<=o+qr},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,sl(s,n.a,n.b)))}},ml=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=sl(s,e.a,e.b),a=Qr(e.a,e.b);return o>=-qr&&o<=1+qr&&il(s,e.a,e.b)<=.02*U&&_t(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-qr&&o*a+i<=a+qr}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>il(s,t.a,t.b)-il(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,sl(s,a.a,a.b)))}:null},gl=(t,e,s)=>{const i=cl(t),{atoms:o,rooms:a}=pl(t,i);ul(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=ht(e.a,e.b,U),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!Vr(t.a)||!Vr(t.b))&&ol(e.a,e.b,t.a,t.b)>=Qr(e.a,e.b)-qr)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new Ur("thickness-conflict",e.key);if(a.length)return a[0];const n=mt(i,e.a,e.b,U,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:ht(t.a,t.b,U),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=_l(t,e);s&&(t.host=s);continue}const i=_l(t,e)??(s?ml(t,e):null);if(i)t.host=i;else{if(!s)throw new Ur("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function fl(t,e={}){const s=JSON.stringify(t),i=Kr(t);lt(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=gl(t,a,s)}return i.model_version=9,lt(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const bl=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function vl(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)bl(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(bl(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):Kr(e)}return o[e,e in t?s(t[e],i):Kr(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return Kr(e)};return s(t,e)}const yl=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),$l=(t,e,s)=>Math.min(s,Math.max(e,t)),wl=t=>Pt({spaces:[t]})[0],kl=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,l=Math.max(i*r*.02,1e-9),h=[];for(const e of t||[])for(const t of V(e)||[])h.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l),p=new Map;for(const n of t||[]){if(!n?.id)continue;const u=H(t,n.id,e,s,i,o,a,r);if(u)for(let t=0;t0&&_>0&&n===m&&_!==n))continue;const g=u.poly[i],f=u.poly[(i+1)%u.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>l&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,h)&&d(f,h))continue;const v=kl(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let u=e.slice();const _=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(_(s,e.a)&&_(i,e.b)||_(s,e.b)&&_(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>kl(t.a,t.b,r).localeCompare(kl(e.a,e.b,r)))){const e=[...t.targets][0],s=u.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;u=u.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(u=et(u,t.a,t.b,e,i,r))}return u}const Sl=t=>{const e=[];for(const s of t||[]){const t=V(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(h),d=new Map;for(const t of h){const e=jr("space",t);e.layers>0&&Wr("space",e.root)&&Gr(d,e.root,t)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=jr("room",t);o.layers>0&&Wr("room",o.root)&&Gr(s,o.root,t),u.has(t)||u.set(t,e),_.has(t)||_.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(u.keys()),f=new Map,b=new Map;for(const t of l)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=jr("space",t).root;if(!Wr("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=jr("room",t).root;if(!Wr("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of l){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),l=!0===t.removed;let h=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,h=i,p=!0,v.spaceRefsRemapped++):l||"virtual"===t.binding?l||(delete t.space,h=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,h=d.spaceId,v.spaceRefsRemapped++):(delete t.space,h=null,v.markersDetached++))}l&&!p||x(t,h,d),r&&p&&n===s&&h&&(o[e]={...a,s:h},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Br(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},z=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),l=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(l)a=T(t,i,"device",String(l.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",_.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,R(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,z.add(i)}else delete o[t],R(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...z].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=gt(n,a.layout);lt(n),ft(a.layout);const l=JSON.stringify(t||{}),h=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&yl(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(yl(o,"segments")&&(delete o.segments,e++),yl(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=kt&&t<=xt&&(yl(o,"plan_scale_x")||(o.plan_scale_x=t),yl(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);yl(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?$l(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)yl(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&yl(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(yl(t,"width")){const s=Number(t.width);(yl(t,"width_cm")||Number.isFinite(s))&&(yl(t,"width_cm")||(t.width_cm=Number($l(s/$t*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(yl(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),yl(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number($l(St*Mt(t)/$t*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(yl(t,"scale")||yl(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Dt(t[1]));if(!(yl(t,"entity")||yl(t,"attr")||yl(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":Ct(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const u=(_=n.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=gr(n.spaces||[],a.layout);let g=0,f=0,b=0,v="";const y=m.spaces.map(t=>{const e=bt(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/U*s;return i>b&&(b=i,v=String(t?.id||"")),e.space}),$=g?gr(y,m.layout):{...m,spaces:y};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,S=0,M=0,D=0,C=0,P=0,I=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=wl(e),r=wl(s);if(!a||!r)continue;const l=Sl(a.rooms),h=Sl(r.rooms),c=.02*$t,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:ht(t.a,t.b,U),cm:0})),p=new Set(d.map(t=>t.key)),_=vt(e,a.rooms,E,c);x+=_.length;for(const t of _){const e=[t[0]/E,t[1]/E],s=[t[2]/E,t[3]/E],i=ht(e,s,U);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=yt(d,l,h,U,E),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*E),s=t.b.map(t=>t*E);return h.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});S+=Math.max(0,m-b.length);const v=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...v,...b.map(({key:t,...e})=>e)];const y=b.map(t=>[t.a[0]*E,t.a[1]*E,t.b[0]*E,t.b[1]*E]),$=Array.isArray(e.walls)?e.walls.length:0;let w=yt(e.walls,l,h,U,E);w=xl(r.rooms,w,y,U,Number(s.cell_cm)>0?Number(s.cell_cm):5,$t,E),w=st(r.rooms,w,y,U,Number(s.cell_cm)>0?Number(s.cell_cm):5,$t,E),w=wt(w,s.rooms||[],U,1,y.map(t=>[t[0]/E,t[1]/E,t[2]/E,t[3]/E])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const T=kr(s.partitions||[],{pitch:U,geometry:Sr(s)});T.merged&&(M+=T.merged,s.partitions=T.partitions,xr(s.openings,s.partitions,T.openingMoves,{coordScale:E,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:$t}));const R=wl(s);if(R){const t=o(s,R,s.walls||[],y,{pitch:U,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:$t,coordScale:E});t.partitionsReconciled&&(D+=t.partitionsReconciled,C+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&I++}let T=0;c<=9&&(T=function(t){const e=fl(t);return vl(t,e.config),{...e,config:t}}(n).migratedSegments);const R=lt(n),z=ft($.layout),A=JSON.stringify(R)!==l||JSON.stringify(z)!==h;c<9&&A&&(R.model_version=9);const F=JSON.stringify(R)!==l||JSON.stringify(z)!==h,N=Number.isInteger(Number(R.model_version))?Number(R.model_version):c,H=F?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},L=F?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:R,layout:z,report:{...H,modelFrom:c,modelTo:N,migrated:F?p:0,glowSpacesMigrated:F?d.glowSpaces:0,glowRoomsMigrated:F?d.glowRooms:0,canonicalized:F?I:0,wallSegmentsMigrated:F?T:0,legacyZeroWallsMigrated:F?x:0,wallsMerged:F?k:0,spansMerged:F?S:0,partitionsMerged:F?M:0,partitionsReconciled:F?D:0,openingsRehosted:F?C:0,redundantDraftsRemoved:F?P:0,wallsStraightened:F?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:F?b:0,maxStraightenSpace:F?v:"",latticeCoordinatesCanonicalized:F?r.canonicalized:0,latticeCoordinatesFar:F?r.far:0,latticeMaxShift:F?r.maxShift:0,latticeMaxShiftCm:F?r.maxShiftCm:0,latticeSpaces:F?r.spaces:[],...L},changed:F}}const Dl=new WeakSet,Cl=t=>{t._regSignature="",t._maybeRebuildDevices(),t.requestUpdate()};async function Pl(t,e,s,i,o,a=""){const n=t.host,r=n._serverCfg;if(!r)return!1;let l=JSON.parse(JSON.stringify(r));l.markers=l.markers||[];let h=l.markers.find(t=>t.id===e);if(!h){const t=n._devices.find(t=>t.id===e);if(!t||"device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return!1;h={id:t.id,binding:`${t.bindingKind}:${t.bindingRef}`,space:t.space||null,area:t.area||null,hidden:!!t.hidden},l.markers.push(h)}h.vacuum=function(t,e){const s=e.matrix.map(t=>Number(t.toFixed(6))),i=t?.map_routes;if(Array.isArray(i)&&i.length){const o=i.find(t=>t.id===e.routeId)||i.find(t=>t.source===e.source&&t.map_id===e.mapId);if(o)return{...t,map_routes:ko(i,o.id,s)}}return{...t,source:e.source,calibration:{...t?.calibration||{},[e.mapId]:s}}}(h.vacuum||{},{source:s,mapId:i,routeId:a,matrix:o}),l=t._prepareConfigCandidate(l);const c=u(r,l,n._cfgContentFingerprint,n._cfgRev,_);n._serverCfg=l,Cl(n),n._saveConfigDebounced.pending()&&n._saveConfigDebounced.cancel();try{return await t._saveConfigNow(c),!0}catch(t){return m(n,c,_),Cl(n),n._showToast(n._t("toast.cfg_save_failed",{err:n._errText(t)})),!1}}function Il(t){return 1===t}function Tl(t){return`${t}-${globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`}function Rl(t){const e=function(t){return JSON.stringify({message:t.message.trim(),contact:t.contact.trim(),previewToken:t.attach&&t.preview?t.preview.token:""})}(t);return{idempotencyKey:t.submissionFingerprint&&t.submissionFingerprint!==e?Tl("report"):t.idempotencyKey,fingerprint:e}}function zl(t){return[...t].length}function Al(t,e=Date.now()){const s=t.message.trim();return s?zl(s)>1e4?"message_too_long":zl(t.contact.trim())>320?"contact_too_long":t.attach&&!t.preview?"preview_missing":t.attach&&t.preview.expiresAt<=e?"preview_expired":null:"message_required"}function Fl(t,e=Date.now()){return"building"!==t.status&&"sending"!==t.status&&"success"!==t.status&&null===Al(t,e)}function Nl(t){const e=function(t){const e=/Firefox\/(\d+)/i.exec(t);if(e)return{family:"firefox",major:Number(e[1])||0};const s=/(?:Chrome|Chromium|Edg)\/(\d+)/i.exec(t);if(s)return{family:"chromium",major:Number(s[1])||0};const i=/Version\/(\d+).+Safari\//i.exec(t);return i?{family:"webkit",major:Number(i[1])||0}:{family:"unknown",major:0}}(t.userAgent??globalThis.navigator?.userAgent??""),s=t.now??Date.now(),i=Number(t.registryLastSuccess||0),o=i<=0?"unknown":s-i<=6e5?"fresh":"stale",a=["en","ru","de","fr"].includes(t.language)?t.language:"en",n=globalThis.matchMedia?.bind(globalThis);return{browser_family:e.family,browser_major:Math.max(0,Math.min(999,e.major)),language:a,coarse_pointer:t.coarsePointer??!!n?.("(pointer: coarse)").matches,hover_capable:t.hoverCapable??!!n?.("(hover: hover)").matches,registry_access:"full"===t.registryAccess?"full":"limited"===t.registryAccess?"partial":"unavailable",registry_age_bucket:o}}function Hl(t){const e=t&&"object"==typeof t&&"code"in t?String(t.code||""):"";return new Set(["support_invalid_message","support_preview_expired","support_package_too_large","support_rate_limited","support_unavailable","support_rejected","unauthorized"]).has(e)?e:"support_unavailable"}const Ll=[{id:"air_conditioner",group:"appliance",art:{d:"M20.5 39h69a2.5 2.5 0 0 1 2.5 2.5V69a2.5 2.5 0 0 1-2.5 2.5h-69A2.5 2.5 0 0 1 18 69V41.5a2.5 2.5 0 0 1 2.5-2.5 M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v8.5H25z M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v3.5H25z",viewW:110,viewH:110}},{id:"armchair",group:"furniture",art:{d:"M42.5 29h25a10 10 0 0 1 10 10v26.5a10 10 0 0 1-10 10h-25a10 10 0 0 1-10-10V39a10 10 0 0 1 10-10 M77.5 48.5a6 6 0 0 1 2 11.657V71.5a4 4 0 0 1-4 4H34a4 4 0 0 1-4-4V60.157A6.001 6.001 0 0 1 32 48.5a6 6 0 0 1 5.419 3.424A4 4 0 0 1 38 54v7h33.5v-6.5q0-.107.005-.213A6 6 0 0 1 77.5 48.5 M42.75 57h24.5A4.75 4.75 0 0 1 72 61.75a4.75 4.75 0 0 1-4.75 4.75h-24.5A4.75 4.75 0 0 1 38 61.75 4.75 4.75 0 0 1 42.75 57M36.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 41.392 82H38a1.5 1.5 0 0 1-1.5-1.5zm36 0h-8l1.674 5.441A1.5 1.5 0 0 0 67.608 82H71a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"bathtub",group:"sanitary",art:{d:"M28 75h8l-1.674 5.441a1.5 1.5 0 0 1-1.434 1.059H29.5A1.5 1.5 0 0 1 28 80zm53 0h-8l1.674 5.441a1.5 1.5 0 0 0 1.434 1.059H79.5A1.5 1.5 0 0 0 81 80zM19.5 46.5H90a2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5H19.5A2.5 2.5 0 0 1 17 49a2.5 2.5 0 0 1 2.5-2.5 M19.5 51.5H90L86.633 68a10 10 0 0 1-9.798 8h-44.17a10 10 0 0 1-9.798-8zM30 36v-2a5 5 0 1 0-10 0v12.5",viewW:110,viewH:110}},{id:"bed",group:"furniture",art:{d:"M16 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 20.892 82H17.5a1.5 1.5 0 0 1-1.5-1.5zm78.5 0h-8l1.674 5.441A1.5 1.5 0 0 0 89.608 82H93a1.5 1.5 0 0 0 1.5-1.5zM25 29h60.5a2.5 2.5 0 0 1 2.5 2.5V68a2.5 2.5 0 0 1-2.5 2.5H25a2.5 2.5 0 0 1-2.5-2.5V31.5A2.5 2.5 0 0 1 25 29 M16 59.5c0-5.523 4.477-10 10-10h58.5c5.523 0 10 4.477 10 10v6H16zm-1 6h80.5a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H15a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1m15.5-20a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4h-21zm28.5 0a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4H59z",viewW:110,viewH:110}},{id:"bidet",group:"sanitary",art:{d:"M36 57.5c0 5.523 4.477 10 10 10h18.5c5.523 0 10-4.477 10-10V53H36zm8.5 22a1 1 0 0 0 1 1H65a1 1 0 0 0 1-1v-12H44.5zm-8.5-32a1 1 0 0 1 1-1h36.5a1 1 0 0 1 1 1V53H36zM50.5 41a1 1 0 0 1 1-1H59a1 1 0 0 1 1 1v5.5h-9.5zM53 30a1 1 0 0 1 1-1h2.5a1 1 0 0 1 1 1v10H53zm-4 6a1 1 0 0 1-1-1v-2.5a1 1 0 0 1 1-1h4V36z",viewW:110,viewH:110}},{id:"boiler",group:"appliance",art:{d:"M46.5 81.5h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zm12.5 0h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zM44 23h22.5a8 8 0 0 1 8 8v42.5a8 8 0 0 1-8 8H44a8 8 0 0 1-8-8V31a8 8 0 0 1 8-8 M36 31a8 8 0 0 1 8-8h22.5a8 8 0 0 1 8 8v42H36z M48.5 42.5a7 7 0 1 0 14 0 7 7 0 1 0-14 0m6.5 0 3.5-3.5",viewW:110,viewH:110}},{id:"chair",group:"furniture",art:{d:"M40.5 39.5C40.5 32.596 46.096 27 53 27h4c6.904 0 12.5 5.596 12.5 12.5v13a5 5 0 0 1-5 5h-19a5 5 0 0 1-5-5z M41.25 54h27.5A3.25 3.25 0 0 1 72 57.25a3.25 3.25 0 0 1-3.25 3.25h-27.5A3.25 3.25 0 0 1 38 57.25 3.25 3.25 0 0 1 41.25 54m28.25 6.5H64l1.88 21.63a1.5 1.5 0 0 0 1.495 1.37H68a1.5 1.5 0 0 0 1.5-1.5zm-29 0H46l-1.88 21.63a1.5 1.5 0 0 1-1.495 1.37H42a1.5 1.5 0 0 1-1.5-1.5z",viewW:110,viewH:110}},{id:"coffee_table",group:"furniture",art:{d:"M25 51h5.238l-1.534 15.646A1.5 1.5 0 0 1 27.211 68H26.5a1.5 1.5 0 0 1-1.5-1.5zm60.238 0H80l1.534 15.646A1.5 1.5 0 0 0 83.027 68h.711a1.5 1.5 0 0 0 1.5-1.5z M24.34 43.485A2.5 2.5 0 0 1 26.625 42h56.75a2.5 2.5 0 0 1 2.285 1.485l2.715 6.109A1 1 0 0 1 87.461 51H22.54a1 1 0 0 1-.914-1.406z M87.5 51h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"computer",group:"appliance",art:{d:"M24.5 38h40a2.5 2.5 0 0 1 2.5 2.5v22a2.5 2.5 0 0 1-2.5 2.5h-40a2.5 2.5 0 0 1-2.5-2.5v-22a2.5 2.5 0 0 1 2.5-2.5m50 0h11a2.5 2.5 0 0 1 2.5 2.5v29a2.5 2.5 0 0 1-2.5 2.5h-11a2.5 2.5 0 0 1-2.5-2.5v-29a2.5 2.5 0 0 1 2.5-2.5M43 65h5v7h-5zm-6 7h16m23-12h8m-8-17h8 M35 56V46h5m-5 7h5 M39.5 53a3.5 3.5 0 1 0 0-7m15.036 8.536a5 5 0 1 1 0-7.071",viewW:110,viewH:110}},{id:"cooktop",group:"appliance",art:{d:"M27.5 29h55a2.5 2.5 0 0 1 2.5 2.5v47a2.5 2.5 0 0 1-2.5 2.5h-55a2.5 2.5 0 0 1-2.5-2.5v-47a2.5 2.5 0 0 1 2.5-2.5 M33.5 66.5a8 8 0 1 0 16 0 8 8 0 1 0-16 0m26.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0m3 23a5 5 0 1 0 10 0 5 5 0 1 0-10 0m-29.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0",viewW:110,viewH:110}},{id:"dining_table",group:"furniture",art:{d:"M83.5 43H78l6.742 30.82A1.5 1.5 0 0 0 86.207 75h.54a1.5 1.5 0 0 0 1.482-1.732zm-56 0H33l-6.742 30.82A1.5 1.5 0 0 1 24.793 75h-.54a1.5 1.5 0 0 1-1.482-1.732zm-2.62-7.406A2.5 2.5 0 0 1 27.21 34h56.58a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044A1 1 0 0 1 87.538 43H23.462a1 1 0 0 1-.932-1.362z M88.5 43h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"dishwasher",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5M43 42h24m-1.5-11a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"dryer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5 M55.994 55.5s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m5.979-11s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m-8.519-11s-.886 1.81-.986 3.143c-.132 1.761 1.066 2.554.986 4.321-.067 1.479-.986 3.536-.986 3.536",viewW:110,viewH:110}},{id:"exercise",group:"other",art:{d:"m60.752 62.03-5.379-2.312 11.066-25.75a1.5 1.5 0 0 1 1.97-.786l2.624 1.127a1.5 1.5 0 0 1 .786 1.97zm-18.648-4.512-4.353 1.189-4.474-16.382a1.5 1.5 0 0 1 1.052-1.842l1.459-.398a1.5 1.5 0 0 1 1.842 1.052z M41.297 38.173a2.215 2.215 0 0 1 0 4.43H29.781a2.215 2.215 0 0 1 0-4.43zm33.219 38.091a2.657 2.657 0 1 1 0 5.315H28.453a2.657 2.657 0 1 1 0-5.315zm8.641-51.366a1.795 1.795 0 0 1 .68 2.41l-3.03 5.59a5 5 0 0 1-4.396 2.618H65.657a1.772 1.772 0 1 1 0-3.544h9.61a3 3 0 0 0 2.598-1.5l2.84-4.917a1.795 1.795 0 0 1 2.452-.657M29.339 65.787c0-6.699 6.457-11.503 12.873-9.579l24.291 7.288a10 10 0 0 1 7.127 9.578v3.19H29.339zm0 15.852h7.086l-1.394 3.486a1.5 1.5 0 0 1-1.393.943h-2.8a1.5 1.5 0 0 1-1.5-1.5zm44.291 0h-7.087l1.395 3.486a1.5 1.5 0 0 0 1.392.943h2.8a1.5 1.5 0 0 0 1.5-1.5z M35.54 66.077a4.872 4.872 0 1 0 9.743 0 4.872 4.872 0 1 0-9.744 0",viewW:110,viewH:110}},{id:"fireplace",group:"other",art:{d:"M26.672 34.425h55.99v39.47h-55.99Z M23 74.894a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v5.802a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zM23 29a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v4.425a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zm15.145 21.44a5 5 0 0 1 5-5h23.043a5 5 0 0 1 5 5v23.454H38.145z M44.57 65.633a2.754 2.754 0 0 1 2.754-2.753H62.01v5.507H47.324a2.754 2.754 0 0 1-2.754-2.754m20.194 5.507a2.754 2.754 0 0 0-2.754-2.753H47.324v5.507H62.01a2.754 2.754 0 0 0 2.754-2.754 M44.57 71.14a2.754 2.754 0 1 0 5.508 0 2.754 2.754 0 1 0-5.508 0m14.688-5.507a2.754 2.754 0 1 0 5.507 0 2.754 2.754 0 1 0-5.507 0",viewW:110,viewH:110}},{id:"fridge",group:"appliance",art:{d:"M35.5 18H75a2.5 2.5 0 0 1 2.5 2.5v64A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5v-64a2.5 2.5 0 0 1 2.5-2.5 M33 46.5h44.5v38A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5zm2.5 44a1 1 0 0 0 1 1h37a1 1 0 0 0 1-1V87h-39zm3-49.5v-9.5m0 30.5v-9.5",viewW:110,viewH:110}},{id:"hood",group:"appliance",art:{d:"M44.308 57h21.384L83 69H27zM44 37.5a2.5 2.5 0 0 1 2.5-2.5h17a2.5 2.5 0 0 1 2.5 2.5V57H44zM80.5 74h-51a2.5 2.5 0 0 1-2.5-2.5V70a1 1 0 0 1 1-1h54a1 1 0 0 1 1 1v1.5a2.5 2.5 0 0 1-2.5 2.5",viewW:110,viewH:110}},{id:"kitchen_cabinet",group:"furniture",art:{d:"M38 88a1 1 0 0 0 1 1h32a1 1 0 0 0 1-1v-3H38zm17-68h16.5a2.5 2.5 0 0 1 2.5 2.5v17a2.5 2.5 0 0 1-2.5 2.5H55zm-19 2.5a2.5 2.5 0 0 1 2.5-2.5H55v22H38.5a2.5 2.5 0 0 1-2.5-2.5zM59 33v5m-8-5v5m4 47h16.5a2.5 2.5 0 0 0 2.5-2.5v-17a2.5 2.5 0 0 0-2.5-2.5H55zm-19-2.5a2.5 2.5 0 0 0 2.5 2.5H55V63H38.5a2.5 2.5 0 0 0-2.5 2.5zM67 67h-5m-14 0h-5",viewW:110,viewH:110}},{id:"kitchen_sink",group:"sanitary",art:{d:"M21.5 84h68a2.5 2.5 0 0 0 2.5-2.5v-35a2.5 2.5 0 0 0-2.5-2.5h-68a2.5 2.5 0 0 0-2.5 2.5v35a2.5 2.5 0 0 0 2.5 2.5 M26 78h26.5a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H26a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m23-24v5m9.5 19H85a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H58.5a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m-6-39.5a1 1 0 0 0-1-1H44a1 1 0 0 0-1 1V44h9.5zM58 33v-2a5 5 0 1 0-10 0v6.5M62 54v5",viewW:110,viewH:110}},{id:"nightstand",group:"furniture",art:{d:"M76.5 73.5h-8l1.674 5.441A1.5 1.5 0 0 0 71.608 80H75a1.5 1.5 0 0 0 1.5-1.5zm-42.5 0h8l-1.674 5.441A1.5 1.5 0 0 1 38.892 80H35.5a1.5 1.5 0 0 1-1.5-1.5z M34.5 30.5h41A2.5 2.5 0 0 1 78 33v40.5a2.5 2.5 0 0 1-2.5 2.5h-41a2.5 2.5 0 0 1-2.5-2.5V33a2.5 2.5 0 0 1 2.5-2.5 M32 39.5h46v18H32Zm18.5 9H60M50.5 66H60",viewW:110,viewH:110}},{id:"oven",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5 M40.5 47h29a2.5 2.5 0 0 1 2.5 2.5v20a2.5 2.5 0 0 1-2.5 2.5h-29a2.5 2.5 0 0 1-2.5-2.5v-20a2.5 2.5 0 0 1 2.5-2.5m25-16a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5M43 42h24",viewW:110,viewH:110}},{id:"plant",group:"other",art:{d:"M45.952 75.849a2.5 2.5 0 0 0 2.485 2.231H63.43a2.5 2.5 0 0 0 2.485-2.231l1.571-14.532H44.381zM60.236 44.78C58.423 36.174 66.805 29.605 77 31.87c0 8.155-8.609 16.763-16.763 12.912m-9.761 5.147c1.517-8.665-7.085-14.943-17.195-12.33.28 8.15 9.178 16.458 17.195 12.33 M55.706 55.88v-2.944m0 0c1.133-5.89 7.09-10.956 12.912-15.404M55.706 52.936c-4.938-4.146-7.982-6.16-14.044-9.061m1.453 12.005h25.636a1 1 0 0 1 1 1v3.437a1 1 0 0 1-1 1H43.115a1 1 0 0 1-1-1v-3.436a1 1 0 0 1 1-1",viewW:110,viewH:110}},{id:"rug",group:"other",art:{d:"M90.27 33.874h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.154h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20M28.12 32h54.025a2.5 2.5 0 0 1 2.5 2.5v40.439a2.5 2.5 0 0 1-2.5 2.5H28.12a2.5 2.5 0 0 1-2.5-2.5V34.5a2.5 2.5 0 0 1 2.5-2.5 M25.62 34.5a2.5 2.5 0 0 1 2.5-2.5h1.717v45.439H28.12a2.5 2.5 0 0 1-2.5-2.5zm59.026 0a2.5 2.5 0 0 0-2.5-2.5H80.43v45.439h1.716a2.5 2.5 0 0 0 2.5-2.5zM37.8 54.485l17.801-8.432 17.801 8.432-17.8 8.432z",viewW:110,viewH:110}},{id:"shelving",group:"furniture",art:{d:"M28 58.5h53V39H28Z M64 58.5h5V47a1 1 0 0 0-1-1h-3a1 1 0 0 0-1 1zM52.5 39h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-12 0h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-5 29.5V71a1 1 0 0 1 1 1H49a1 1 0 0 1 1-1v-2.5a1 1 0 0 1-1-1H36.5a1 1 0 0 1-1 1m33.5-10h7v-14a1 1 0 0 0-1-1h-5a1 1 0 0 0-1 1zM47.5 39h-7V25a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1zm-12 34v4a1 1 0 0 1 1 1h14a1 1 0 0 1 1-1v-4a1 1 0 0 1-1-1h-14a1 1 0 0 1-1 1M28 78h53v7H28Z M28 89.5V23a2.5 2.5 0 0 1 2.5-2.5h48A2.5 2.5 0 0 1 81 23v66.5",viewW:110,viewH:110}},{id:"shower",group:"sanitary",art:{d:"M31 84h49a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H31a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1m31.5-47.6v-4.686a11.7 11.7 0 0 0-3.441-8.283A11.77 11.77 0 0 0 50.75 20a11.77 11.77 0 0 0-8.309 3.431A11.7 11.7 0 0 0 39 31.714V84 M72 39.25a10.25 10.25 0 1 0-20.5 0zm-17.25 5v2.5m7.25-2.5v2.5m7.25-2.5v2.5m-14.5 3.75V53M62 50.5V53m7.25-2.5V53m-14.5 3.75v2.5m7.25-2.5v2.5m7.25-2.5v2.5M54.75 63v2.5M62 63v2.5m7.25-2.5v2.5",viewW:110,viewH:110}},{id:"sink",group:"sanitary",art:{d:"M30 57c0 5.523 4.477 10 10 10h29.5c5.523 0 10-4.477 10-10v-6.5A2.5 2.5 0 0 0 77 48H32.5a2.5 2.5 0 0 0-2.5 2.5zm14 22a1 1 0 0 0 1 1h19.5a1 1 0 0 0 1-1V67H44zm6-36.5a1 1 0 0 1 1-1h7.5a1 1 0 0 1 1 1V48H50zM45 37v-2a5 5 0 1 1 10 0v6.5",viewW:110,viewH:110}},{id:"sofa",group:"furniture",art:{d:"M59.5 29h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5m-34 0h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5 M18.5 44a5 5 0 0 1 5 5v12H86V49a5 5 0 0 1 5-5h.5a5 5 0 0 1 5 5v21.5a5 5 0 0 1-5 5H18a5 5 0 0 1-5-5V49a5 5 0 0 1 5-5z M25.25 57h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 25.25 57m34 0h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 59.25 57M18.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 23.392 82H20a1.5 1.5 0 0 1-1.5-1.5zm72 0h-8l1.674 5.441A1.5 1.5 0 0 0 85.608 82H89a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"stairs",group:"other",art:{d:"M73.47 46.365v-17.62m-10.571 25.55V36.674M52.326 61.783V45.484M42.193 69.273v-15.86M28.979 77.761a1 1 0 0 1 1-1h53.624v6.489a1 1 0 0 1-1 1H29.98a1 1 0 0 1-1-1z M26.777 57.497a1 1 0 0 1 1-1h2.846a1 1 0 0 1 1 1V83.25a1 1 0 0 1-1 1h-2.846a1 1 0 0 1-1-1z m73.113 28.633-41.492 32.93v-4.405l39.9-32.007a.7.7 0 0 1 .428-.151h3.724a1.762 1.762 0 1 1 0 3.524h-2.248a.5.5 0 0 0-.312.109M25.895 53.414a3.084 3.084 0 1 0 6.167 0 3.084 3.084 0 1 0-6.167 0m14.978 16.859a1 1 0 0 1 1-1h41.73v7.488h-42.73zm10.131-7.489a1 1 0 0 1 1-1h31.598v7.488H51.004z M61.576 55.294a1 1 0 0 1 1-1h21.026v7.49H61.576z M72.15 47.806a1 1 0 0 1 1-1h9.454a1 1 0 0 1 1 1v6.489H72.15z",viewW:110,viewH:110}},{id:"toilet",group:"sanitary",art:{d:"M32 26.5a2.5 2.5 0 0 1 2.5-2.5H45a2.5 2.5 0 0 1 2.5 2.5V57H32zM47.5 52h29a2.5 2.5 0 0 1 2.5 2.5V57H47.5zm17.037 20.102C73.95 70.288 79 62.01 79 57H32c.5 7.5 8.072 7 7 11l-2.342 16.862a1 1 0 0 0 .99 1.138H66.72a1 1 0 0 0 .97-1.243z",viewW:110,viewH:110}},{id:"tv",group:"appliance",art:{d:"M23.5 31H86a2.5 2.5 0 0 1 2.5 2.5v34A2.5 2.5 0 0 1 86 70H23.5a2.5 2.5 0 0 1-2.5-2.5v-34a2.5 2.5 0 0 1 2.5-2.5M50 70h9.5v8H50Zm-10 8h30M42.5 45.5h5m5 0h-5m0 0v9.75 m56.75 45.5 5 10 5-10",viewW:110,viewH:110}},{id:"wardrobe",group:"furniture",art:{d:"M28 23a2.5 2.5 0 0 1 2.5-2.5h24V85h-24a2.5 2.5 0 0 1-2.5-2.5zm2 65.5a1 1 0 0 0 1 1h47a1 1 0 0 0 1-1V85H30zM81 23a2.5 2.5 0 0 0-2.5-2.5h-24V85h24a2.5 2.5 0 0 0 2.5-2.5zM50.5 48v9.5m8-9.5v9.5",viewW:110,viewH:110}},{id:"washer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"work_table",group:"furniture",art:{d:"M88.5 47h-20v14h20zm-12 4h4m7.984-8h-66v4h66zm-66 4h4v26.5a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5z M24.866 35.597A2.5 2.5 0 0 1 27.197 34h56.577a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044a1 1 0 0 1-.93 1.362H23.46a1 1 0 0 1-.933-1.361zM88.5 61h-20v12.5A1.5 1.5 0 0 0 70 75h17a1.5 1.5 0 0 0 1.5-1.5zm-12 4h4",viewW:110,viewH:110}}];class El{constructor(t,e){this.host=t,this.hooks=e}place(t,e="mouse"){const s=this.host._decorImagePalette,i=this.host._curSpaceCfg;if(!s||!i)return;const o=this.hooks.decorSnap(t,e),a=It(s.width,s.height),n=Tt(a.w,this.host._cellCm,this.host._gridPitch)/E,r=Tt(a.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,l="di"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),h=this.hooks.geometrySnapshot();i.decor=[...this.host._decorList,{id:l,kind:"image",asset_id:s.asset_id,x:A(o[0]/E-n/2),y:A(o[1]/this.host._decorH-r/2),w:n,h:r,opacity:1}],this.host._decorSel=l,this.host._decorTool="select",this.host._decorImagePalette=null,this.hooks.clearFurniturePreview(),this.hooks.recordGeometry(this.host._t("history.decor_add"),h),this.hooks.saveConfig(),this.host.requestUpdate()}renderPlacementPreview(){const t=this.host._furnPreviewInput,e=this.host._decorImagePalette;if(!t||!e||"decor"!==this.host._mode||"image"!==this.host._decorTool||!this.host._pointerModality.hoverEnabled)return d;const s=this.hooks.decorSnap(t.raw,"mouse"),i=It(e.width,e.height),o=Tt(i.w,this.host._cellCm,this.host._gridPitch)/E,a=Tt(i.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,n=this.host._display(e.url);return n?Rt``:d}renderMissing(t,e,s,i,o,a,n,r,l){const h=i+a/2,c=o+n/2;return Rt` - ${this.host._t("decor.image_unavailable")} - - - ${this.host._t("decor.image_unavailable")} - `}async uploadFromInput(t,e=!1){const s=t.target,i=s.files?.[0];if(s.value="",!i||this.host._decorAssetBusy)return;let o;try{o=await so(i,2097152)}catch{return void this.host._showToast(this.host._t("backup.error.invalid_image"))}if("reject"!==o.kind)return"guard"===o.kind?(this.hooks.setGuardReplace(e),void(this.host._backdropGuard=o.state)):void await this.upload(i,i.name,e);this.host._showToast(this.host._t("toast.plan_formats"))}async upload(t,e,s){if(!this.host._decorAssetBusy){this.host._decorAssetBusy=!0;try{const i=new FormData;i.append("file",t,e);const o=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/assets/upload",{method:"POST",body:i}):await fetch("/api/houseplan/assets/upload",{method:"POST",body:i,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await o.json().catch(()=>({}));if(!o.ok||a.error||!a.asset){const t={invalid_format:"backup.error.invalid_image",invalid_image:"backup.error.invalid_image",unsupported_image:"backup.error.unsupported_image",too_large:"backdrop.too_large_title",capacity_exceeded:"decor.image_error_capacity"};throw new Error(this.host._t(t[String(a.error)]||"backup.error.io_error"))}const n=zt({assets:[a.asset]}).values().next().value;if(!n)throw new Error(this.host._t("backup.error.io_error"));this.host._decorAssets=new Map(this.host._decorAssets).set(n.asset_id,n),this.host._decorAssetCatalog=[n,...this.host._decorAssetCatalog.filter(t=>t.asset_id!==n.asset_id)],s&&"image"===this.host._decorShapeDialog?.kind?this.host._decorShapeDialog={...this.host._decorShapeDialog,assetId:n.asset_id}:this.host._decorImagePalette=n,this.host.requestUpdate()}catch(t){this.host._showToast(this.host._t("decor.image_upload_failed",{err:this.host._errText(t)}))}finally{this.host._decorAssetBusy=!1}}}async delete(t){if(t.used_by?.length)return void this.host._showToast(this.host._t("decor.image_in_use"));if(await this.host._confirmDanger({key:`delete-decor-asset:${t.asset_id}`,kind:"destructive",title:this.host._t("decor.image_delete_title"),message:this.host._t("decor.image_delete_message",{name:t.name}),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}))try{await this.host.hass.callWS({type:"houseplan/assets/delete",asset_id:t.asset_id}),this.host._decorAssetCatalog=this.host._decorAssetCatalog.filter(e=>e.asset_id!==t.asset_id),this.host._decorAssets.delete(t.asset_id),this.host._decorImagePalette?.asset_id===t.asset_id&&(this.host._decorImagePalette=null),this.host.requestUpdate()}catch(t){this.host._showToast("in_use"===t?.code?this.host._t("decor.image_in_use"):this.host._t("backup.error.io_error"))}}async loadCatalog(){try{const t=await this.host.hass.callWS({type:"houseplan/assets/list"}),e=zt(t);this.host._decorAssetCatalog=[...e.values()],this.host._decorAssets=new Map([...this.host._decorAssets,...e]),this.host.requestUpdate()}catch{this.host._showToast(this.host._t("backup.error.io_error"))}}renderImagePalette(){const t=this.host._decorImagePalette;return p``}renderFurniturePalette(){const t=this.host._furnPalette,e=this.host._furnCategory,s=At.flatMap(t=>Ft(t)),i=Ll.filter(t=>s.some(e=>e.category===t.id)),o=i.find(t=>t.id===e)||null,a=o?s.filter(t=>t.category===o.id):[],n=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`
    t.stopPropagation()}> -
    - ${this.host._t("furn.title")} - - -
    -
    - ${o?p` - -
    - ${this.host._t(`furn.cat_${o.id}`)} -
    -
    - ${a.map(e=>p``)} -
    - `:At.map(t=>p` -
    ${this.host._t(`furn.group_${t}`)}
    -
    - ${i.filter(e=>e.group===t).map(t=>p``)} -
    `)} -
    - ${t?p`
    - - this.host._furnPalette={...t,w:this.hooks.furnFieldToCm(Number(e.target.value))}} /> - - this.host._furnPalette={...t,h:this.hooks.furnFieldToCm(Number(e.target.value))}} /> - ${this.host._t("furn.place_hint")} -
    `:p`
    ${this.host._t("furn.pick_hint")}
    `} -
    `}furnitureSizeInput(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i=Nt(e,this.host._imperial,Ht*this.host._cellCm),o="w"===t?"sizeWField":"sizeHField",a="w"===t?"sizeWCm":"sizeHCm",n="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[o]:e,...null===i?{}:{[a]:Math.abs(i),[n]:i<0}}}furnitureFlip(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i="w"===t?s.sizeWField:s.sizeHField,o=Nt(i,this.host._imperial,Ht*this.host._cellCm),a="w"===t?s.sizeWCm:s.sizeHCm,n=Math.abs(o??a??.1),r="w"===t?"sizeWField":"sizeHField",l="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[r]:Lt(n,e,this.host._imperial),[l]:e}}renderShapeDialog(){const t=this.host._decorShapeDialog,e="rect"===t.kind||"ellipse"===t.kind,s=this.host._t(`decor.${t.kind}`),i=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m"),o="furniture"===t.kind||"image"===t.kind?Nt(t.sizeWField,this.host._imperial,Ht*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?Nt(t.sizeHField,this.host._imperial,Ht*this.host._cellCm):null,n=!("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a),r="image"===t.kind?this.host._decorAssetCatalog.find(e=>e.asset_id===t.assetId)||this.host._decorAssets.get(t.assetId||""):null;return p`this.host._decorShapeDialog=null}> -
    - ${"furniture"===t.kind?p` - - `:"image"===t.kind?p` - - - ${r?p`
    - ${r.name} - ${r.name} -
    `:d} - `:d} - ${"image"===t.kind?p` - - this.host._decorShapeDialog={...t,opacity:Number(e.target.value)}} />`:p`this.host._decorShapeDialog={...t,...e.detail}}>`} - ${"image"!==t.kind?p` -
    this.host._decorShapeDialog={...t,widthCm:this.host._decorSmallCm(Number(e.target.value))}} />${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    `:d} - ${"line"===t.kind?p` - -
    - - -
    - -
    this.host._decorShapeDialog={...t,lengthCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ${i}
    `:"furniture"===t.kind||"image"===t.kind?p` - -
    this.furnitureSizeInput("w",t.target.value)} /> - ×this.furnitureSizeInput("h",t.target.value)} /> - ${i}
    - - `:p` - -
    this.host._decorShapeDialog={...t,sizeWCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ×this.host._decorShapeDialog={...t,sizeHCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ${i}
    `} - - this.host._decorShapeDialog={...t,angle:e.target.value}} /> - ${e?p` - this.host._decorShapeDialog={...t,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}>`:d} -
    -
    - - - -
    -
    `}}const Ol=(t,e)=>t[0]*e[1]-t[1]*e[0],Bl=(t,e)=>t[0]*e[0]+t[1]*e[1],Wl=(t,e)=>[t[0]-e[0],t[1]-e[1]],jl=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Gl(t,e,s){return Math.abs(Ol(t,e))<=s}function ql(t,e,s){return Math.abs(Ol(Wl(t,e),s))}function Ul(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const l=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Gl(l,[i/n,a/n],1e-6)))return[];if(ql(e.a,t,l)>1.1*Math.max(s,o)||ql(e.b,t,l)>1.1*Math.max(s,o))return[];return[[Bl(Wl(e.a,t),l),Bl(Wl(e.b,t),l)]]}),o).flatMap(([e,i])=>{const o=Bt({a:jl(t,l,e),b:jl(t,l,i),halfDepth:s});return o?[o]:[]})}function Kl(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=H(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=Et(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:jl([t.x,t.y],s,-i),b:jl([t.x,t.y],s,i)}}),a=[];for(const i of Ot(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Ul(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=nt(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Ul(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function Vl(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=jl(s.center,a,o),r=[];for(let e=0;ei&&Gl(s.axis,[l[0]/h,l[1]/h],1e-6)))continue;if(ql(o,n,s.axis)>i||ql(a,n,s.axis)>i)continue;const c=Bl(Wl(o,n),s.axis),d=Bl(Wl(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const l=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!l)return null;const[h,c]=l,d=jl(n,s.axis,-s.half),p=jl(n,s.axis,s.half),u=Math.max(0,-s.half-h),_=Math.max(0,c-s.half),m=u>0?jl(n,s.axis,h):d,g=_>0?jl(n,s.axis,c):p,f=Bl(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,u),right:b(p,g,_)}}function Jl(t){const e=jl(t.center,t.axis,-t.half),s=jl(t.center,t.axis,t.half),i=jl(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=jl(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Yl(t,e,s,i){const o=Wl(s,e),a=Bl(o,o);if(!(a>i*i))return Math.hypot(...Wl(t,e))<=i;const n=Bl(Wl(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=jl(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Wl(t,r))<=i}function Zl(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=h>t[1]&&t[0]<(l-n)*(t[1]-r)/(h-r)+n&&(i=!i)}return i}function Xl(t,e,s,i,o,a){const n=Wl(i,s),r=Ol(e,n);if(Math.abs(r)<=a)return null;const l=Wl(s,t),h=Ol(l,n)/r,c=Ol(l,e)/r;return h<-a||h>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,h))}function Ql(t,e,s){const i=Jl(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Zl(t,r.body,a))return 0;for(let i=0;iBl(Wl(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Wl(n,a),l=Math.hypot(r[0],r[1]);if(!(l>s&&Gl(e.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(ql(a,e.center,e.axis)>s||ql(n,e.center,e.axis)>s)continue;const h=Bl(Wl(a,e.center),e.axis),c=Bl(Wl(n,e.center),e.axis),d=Math.min(h,c),p=Math.max(h,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||u2)return Jl(s);if(i.length){const t=i.map(({room:t,edge:i})=>Vl(t,i,s,e.epsilon));if(t.some(t=>!t))return Jl(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Ql(t,e,s)}const eh="1.72.0-beta.2",sh={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},ih={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},oh=2e3,ah=1e3,nh=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},rh=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},lh=1e3,hh=2e3,ch=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},dh="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";class ph{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._supportPreviewGeneration=0,this._decorAssetGuardReplace=null,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=nh(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Wt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?Wt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=nh(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&jt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&Gt(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=qt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=nh(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const l=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,l||this.host.requestUpdate()},this._deletePhysicalSelection=async()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void await this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=async()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._space;if(!t||!this.host._curSpaceCfg)return;if(!await this.host._confirmDanger({key:"delete-draft",kind:"destructive",title:this.host._t("confirm.delete_draft_title"),message:this.host._t("confirm.delete_draft_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")})||this.host._space!==e)return;const s=this.host._curSpaceCfg;if(!s||!(s.room_drafts||[]).some(e=>e.id===t))return;const i=this._geometrySnapshot();s.room_drafts=(s.room_drafts||[]).filter(e=>e.id!==t),s.room_drafts.length||delete s.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),i)},this._deleteDraftSegment=async()=>{const t=this.host._physicalDialog,e=this.host._space;if(!t||"draft"!==t.kind||!this.host._curSpaceCfg)return;const s=t.id,i=t.segment||0,o=await this.host._confirmDanger({key:"delete-draft-segment",kind:"destructive",title:this.host._t("confirm.delete_draft_segment_title"),message:this.host._t("confirm.delete_draft_segment_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._physicalDialog;if(!o||this.host._space!==e||"draft"!==a?.kind||a.id!==s||(a.segment||0)!==i)return;const n=this.host._curSpaceCfg;if(!n)return;const r=(n.room_drafts||[]).findIndex(t=>t.id===s);if(r<0)return;const l=n.room_drafts[r];if(!Array.isArray(l.segments)||!Number.isInteger(i)||i<0||i>=l.segments.length)return;const h=i,c=[],d=l.points.slice(0,h+1),p=l.points.slice(h+1);if(d.length>=2&&c.push({id:l.id,points:d,segments:l.segments.slice(0,h)}),p.length>=2&&c.push({id:c.length?`${l.id}-${Date.now().toString(36)}`:l.id,points:p,segments:l.segments.slice(h+1)}),2===c.length&&n.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const u=this._geometrySnapshot();n.room_drafts.splice(r,1,...c),n.room_drafts.length||delete n.room_drafts,this.host._activeDraftId===l.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=c.length?{kind:"draft",id:c[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),u)},this._furnShiftListener=t=>{if("Shift"!==t.key)return;const e=this.host._furnPreviewInput;if(!e)return;const s="keydown"===t.type;e.free!==s&&(this.host._furnPreviewInput={...e,free:s},this.host.requestUpdate())},this._furnShiftAttached=!1,this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=Zn(Z(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>hh)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot(),h=Ut(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*lh,Number(e.a?.[1])*lh],i=[Number(e.b?.[0])*lh,Number(e.b?.[1])*lh];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/lh,a.interval.a[1]/lh],b:[a.interval.b[0]/lh,a.interval.b[1]/lh],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*lh,i.a[1]*lh],b:[i.b[0]*lh,i.b[1]*lh]};e.host={kind:"partition",id:i.id,t:Xn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(h,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>hh)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=xn(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,X);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Xt(this.host._settings,{bgColor:null})||null,northDeg:Zt(this.host._settings,{}),bgMode:Yt(this.host._settings,{}),sunRays:Jt(this.host._settings,{}),showRoomTooltip:Vt(this.host._settings),zigbeeTopology:Kt(this.host._settings),busy:!1}},this._openSupportDialog=()=>{this.host._norm&&this.host._canEdit&&(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPreviewGeneration+=1,this.host._supportDialog={draftId:Tl("draft"),idempotencyKey:Tl("report"),submissionFingerprint:"",contact:"",message:"",attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:"",reportId:""})},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._decorStylePersistTimer=null,this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Qt).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},this._discoveryPreviewMemo=null,this._decorImages=new El(t,{decorSnap:(t,e)=>this._decorSnap(t,e),geometrySnapshot:()=>this._geometrySnapshot(),clearFurniturePreview:()=>this._clearFurniturePreview(),recordGeometry:(t,e)=>this._recordGeometry(t,e),saveConfig:()=>this._saveConfig(),saveShape:()=>this._decorSaveShape(),setGuardReplace:t=>{this._decorAssetGuardReplace=t},furnShiftDetach:()=>this._furnShiftDetach(),furnPick:t=>this._furnPick(t),furnFieldValue:t=>this._furnFieldValue(t),furnFieldToCm:t=>this._furnFieldToCm(t)}),t._editorSecondary=new rr({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Da}_help(t){const e=`${t}.aria`,s=l(this.host.hass,this.host._config?.language);return te(s,t)&&te(s,e)?p``:d}_setMode(t,e=!0){t!==this.host._mode&&this.host._cancelDangerConfirm(),this.host._endTabDrag(),this._clearFurniturePreview(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if("devices"===this.host._mode&&this.host._cancelDeviceDrag(),this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const l=!s.bg&&"view"===t!=("view"===i),h=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=h?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=h?h.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=h?h.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}l&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?ee([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const u=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(u,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&u===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=se(X,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:se(e,this.host._imperial),max:se(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=kr(s,{pitch:U,seedIds:e,geometry:Sr(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,xr(t.openings,t.partitions,i.openingMoves,{coordScale:lh,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=Sn(this.host._path,xn(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,X)),s=e.length;if((t.partitions||[]).length+s>hh)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=hh:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[ie(oe(t[0],e)),ie(oe(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?ae(s,t,this.host._gridPitch,Xi):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=at(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=gn({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=mn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=gn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?wn(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(rn(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??nn,o=s.excludePoints||[],a=yn(vn(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=G([e[0],e[1]],t);if(r>s.tolerance)continue;const l=$n(a,e,s.gridStep);!fn(l,o,i)&&bn(r,a.key,n)&&(n={kind:"line",point:l,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,l=r?[...r.point]:this._snapDrawPoint(t,e),h=i?ne(i,l):l;return{point:h,candidate:r&&rt(h,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return rt(t,e)}_dropLegacySegments(t=this.host._serverCfg){for(const e of t?.markers||[])"ripple"===e.display&&(e.display="icon_ripple");for(const e of t?.spaces||[]){delete e.segments;const t=new Set,s=e=>"string"==typeof e&&e.length>=1&&e.length<=64&&!t.has(e),i=e=>(t.add(e),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Ht);if(Array.isArray(e.partitions)&&(e.partitions=e.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),e.partitions.length||delete e.partitions),Array.isArray(e.wall_columns)&&(e.wall_columns=e.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:re(t.angle)}})),e.wall_columns.length||delete e.wall_columns),Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=tl(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),e.room_drafts.length||delete e.room_drafts),Array.isArray(e.walls)){const t=le(Pt({spaces:[e]}),e.id),s=t?he(e,t,lh,.02*$t).contour.map(t=>t.map(t=>t/lh)):ct(e.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);e.walls=wt(e.walls,e.rooms||[],U,1,s),e.walls.length||delete e.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=_(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_prepareConfigCandidate(t){return this._dropLegacySegments(t),ce(t)}_writeConfig(t=null){this.host._writesPending++,this.host._writeChain=de(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;const e=_(this.host._serverCfg),s=this._prepareConfigCandidate(this.host._serverCfg),i=_(s);t&&e===t.attemptedFingerprint&&Object.assign(t,{revision:this.host._cfgRev,attempted:s,attemptedFingerprint:i});const o=[...this.host._pendingPhysicalWrites.entries()];for(const[t,e]of o){const i=s.spaces.find(e=>e.id===t),o=me(i);if(o===e.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(s,t).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(e.before),this.host._pendingPhysicalWrites.delete(t),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});e.fingerprint=o}i!==_(this.host._serverCfg)&&(this.host._serverCfg=s),this.host._cfgContentFingerprint=i;try{await this.host._sendConfigCandidate(s)}catch(t){if(!this._rollbackRejectedPhysicalWrites(o))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of o)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),l=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=l.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof Ur?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...qa(a),...Ya(a,o,U),...Xa(a,o,U)];let r=null;try{r=pe(i.rooms||[],i.walls||[],[],U,o,U,1)}catch{r=null}let l="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!l&&r&&r.nodes.length)try{const t=ue(i.rooms||[],i.walls||[],[],[],U,o,U,1);l="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{l=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=_e(i.rooms||[],e,i.walls||[],[],U,o,U,1,l??void 0,r)}catch{s=null}n.push(...Qa(e,s,o,U))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=me(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:fl(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let l=[];try{l=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(l,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||me(e)===me(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let l=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new Ur("invalid-room",e.spaceId);const s=fl(t).config;l=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new Ur("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),h=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of h?.wall_segments||[])t.cm=mt(o.walls,t.a,t.b,U,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=pl(s,cl(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=V(e),l=(s.rooms||[]).find(e=>e.id===t.id),h=V(l),c=V(n);if(r&&h&&c&&r.length===h.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;aqr||il(r,s,i)>qr)continue;const l=sl(n,s,i),h=sl(r,s,i);l<-qr||h>1+qr||h<=l+qr||o.push({start:l,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,h[e],h[(e+1)%h.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/lh,t[1]/lh]):[];i.push(...s.map(t=>[t[0],t[1]])),h=el(t)<=el(l,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{h=!1}if(!h)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;vl(i,n),this._recordGeometry(t,l);const p=i.spaces.find(t=>t.id===e.spaceId),u=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:u?.before||l,fingerprint:me(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this._clearFurniturePreview(),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._furnTouchPending?.pid===t.pointerId)return this._clearFurniturePreview(),this.host._pointers.delete(t.pointerId),void this.host.requestUpdate();if(this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=me(s)!==me(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=fl(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;vl(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:me(n)})}else vl(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=V(e);return!!s&&ge(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=V(e);return!!s&&I(t,s)})}_pointInRoom(t,e){return e.poly?fe(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!ta(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>hh)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=ne(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this.host._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const h=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(h)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),l=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),h=this.host._path[this.host._path.length-1],c=this._samePt(h,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),u=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],_=[...p,...null==d?[]:[{cm:d}],...l],m=u.length>=4&&this._samePt(u[0],u[u.length-1]),g=u,f=_;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||R(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/lh,t[1]/lh]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=xn(i,e,this.host._drawWallCm,X);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*lh,Number(t?.[1])*lh]):[],o=Array.isArray(e?.segments)?e.segments:[];return xn(Math.max(0,t.length-1),s,this.host._drawWallCm,X).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/lh,t[1]/lh]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=Kn(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=ar(r,{requiredSourceKey:o,maxDistance:nt(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=V(t);if(!t.id||!e)continue;const s=be(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=R(i)>=R(o)?i:o,r=a===i?o:i,l=[...n].sort((t,e)=>Math.abs(t.area-R(r))-Math.abs(e.area-R(r))||t.key.localeCompare(e.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const l=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=Vn(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=ar(this._wallGraphSources([]),{point:t,maxDistance:nt(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=ar(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(Wt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qt(s),angle:0},a=(i.wall_columns||[]).find(t=>jt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/lh,e[1]/lh]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:se(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:se(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?re(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:se(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),ch(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())ve(o,ye(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=$e(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&ve(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),l=this.host._physicalPickCycle,h=a.length>1&&l?.signature===n&&r-l.at>380&&r-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:h,x:t.clientX,y:t.clientY,at:r},e=a[h].kind,s=a[h].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Ht*lh-Math.min(...o),Math.min(Ht*lh-Math.max(...o),s[0])),Math.max(-Ht*lh-Math.min(...a),Math.min(Ht*lh-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/lh,o.a[1]/lh],t.b=[o.b[0]/lh,o.b[1]/lh];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=J(t,[o],lh,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,Y(t,s,lh))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&jt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/lh,(o.center[1]+e.delta[1])/lh])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),ch(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:re(e.angle),angle:re(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=re(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&jt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?V(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:nt(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*lh,t.a[1]*this.host._spaceH],b:[t.b[0]*lh,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?nt(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!ta(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],l=n.poly[(i+1)%n.poly.length],h=oa(r,l,a);if(!h)return{enabled:!1,reason:"diagonal"};if(!la(n.poly,i,h,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(da(t,r,l,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=Yo(Ko(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!ta(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!la(t.room.poly,t.edge,h,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(Xo([t.x,t.y],r,l)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=_a(t,e,o,1,a),l=_a(t,e,n,0,a);if(!r||!l)return{enabled:!1,reason:"partial-shared"};v.push(r,l)}const y={roomId:s,edge:i,a:[...r],b:[...l],n:Qo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!$a(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>$a(t,e,y,s,o))){if(!s.some(s=>fa(ba(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>$a(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>$a(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const l=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/lh,t[1]/l]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/lh,s.y=e[1]/l)}const h=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*lh,t[1]*l]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/lh,i[t][1]/lh],o.b=[i[(t+1)%i.length][0]/lh,i[(t+1)%i.length][1]/lh])}}if(h.length&&Array.isArray(r.walls)&&r.walls.length){const t=we(r.walls,h,c,this.host._wallKeyPitch,lh,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=V(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),u=new Map;for(const t of n.walls||[]){const e=p(t);u.set(e,(u.get(e)||0)+1)}const _=[];for(const t of r.walls||[]){const e=p(t),s=u.get(e)||0;s?u.set(e,s-1):_.push(t)}if(ke(_,d,this.host._wallKeyPitch,lh,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=xe(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:_([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};rh(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));ch(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,l=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,h=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:h,before:JSON.parse(h),wallUnionBefore:l})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};rh(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):Se(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const l="mi"===this.host.hass?.config?.unit_system?.length,h=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),u=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this.host._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?Me(b>8?2.5:b,f,u.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of h){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?De(Ce(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:Pe(n,this.host._gridPitch,this.host._cellCm),h=Ie(r,l),p=Pa({poly:a,edge:e.edgeByRoom[s],text:h,view:g,gearCenter:Te(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:h,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=Re(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>ze(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return d;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return Rt``}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),s=e/2,i=t=>t.toFixed(1),o=`M ${i(-.7*s)} 0 H ${i(.7*s)} M 0 ${i(-.22*s)} V ${i(-s)} M ${i(-.32*s)} ${i(-.6*s)} L 0 ${i(-s)} L ${i(.32*s)} ${i(-.6*s)} M 0 ${i(.22*s)} V ${i(s)} M ${i(-.32*s)} ${i(.6*s)} L 0 ${i(s)} L ${i(.32*s)} ${i(.6*s)}`,a=[],n=this._rszRooms(),r=this.host._resize.snapshotIdentity||this._rszSnapshot();for(const t of n)for(let s=0;sthis._rszEdgeDown(e,t.id,s)} - @pointermove=${t=>this._rszMove(t)} - @pointerup=${t=>this._rszUp(t)} - @pointercancel=${t=>this._rszPointerCancel(t)} - @lostpointercapture=${t=>this._rszPointerCancel(t)} - @click=${u?t=>this._rszDisabledActivate(t,p.reason):null} - @keydown=${u?t=>this._rszDisabledKey(t,p.reason):null}> - ${_} - `),a.push(Rt``)}return Rt`${a}`}_partitionOpeningCuts(t=this.host._spaceModel(),e=()=>!0){if(!t)return[];const s=this.host._curSpaceCfg?.id===t.id?this.host._curSpaceCfg:null,i=Ae(s,t,this.host._cellCm,this.host._gridPitch,lh);return Fe(i,e)}_decorSnapGeometry(t){const e=this.host._spaceModel();if(!e)return{points:[],segments:[]};const s=t||"",i=this.host._decorSnapCache;if(i&&i.epoch===this.host._cfgEpoch&&i.space===this.host._space&&i.height===this.host._decorH&&i.exclude===s)return i.geometry;const o=[];for(const e of this.host._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*lh,e.y1*this.host._decorH],s=[e.x2*lh,e.y2*this.host._decorH];o.push({points:[t,s,[(t[0]+s[0])/2,(t[1]+s[1])/2]],segments:[{a:t,b:s}]})}else if("text"===e.kind)o.push({points:[[e.x*lh,e.y*this.host._decorH]],segments:[]});else{const t=this.host._decorBoxOf(e);t&&o.push(Ne(t))}for(const t of e.rooms){const e=V(t);e?.length&&o.push({points:e.flatMap((t,s)=>{const i=e[(s+1)%e.length];return[t,[(t[0]+i[0])/2,(t[1]+i[1])/2]]}),segments:e.map((t,s)=>({a:t,b:e[(s+1)%e.length]}))})}const a=He(o);return this.host._decorSnapCache={epoch:this.host._cfgEpoch,space:this.host._space,height:this.host._decorH,exclude:s,geometry:a},a}_decorSnap(t,e="mouse",s){const i=this.host._stageEl,o=this.host._viewOr(this.host._baseVb()),a="touch"===e||"pen"===e?14:8,n=i?o.w/Math.max(1,i.clientWidth)*a:this.host._gridPitch;return Le(t,this._decorSnapGeometry(s),n,t=>this._snap(t)).point}_replaceDecor(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>s.id===t?{...s,...e}:s),this.host.requestUpdate())}_cancelDecorGesture(){const t=this.host._decorMove?.before||this.host._dtDrag?.before||this.host._bdDrag?.before,e=t&&this.host._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const s=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=s(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,s(t.plan_transform||{})),this.host._cfgEpoch++}this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null,this.host.requestUpdate()}_decorPointerDown(t){const e=this.host._decorTool;if("mouse"===t.pointerType&&0!==t.button&&"select"!==e&&"erase"!==e)return!1;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const s=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorDraft={kind:e,a:s,b:s,pid:t.pointerId},ch(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorTextDialog={x:A(e[0]/lh),y:A(e[1]/this.host._decorH),text:"",color:this.host._decorStyle.color,opacity:this.host._decorStyle.opacity,angle:"0",sizeCm:Ee(St,this.host._cellCm,this.host._gridPitch)},this.host._decorTextSelection={start:0,end:0},!0}if("furniture"===e||"image"===e){if("furniture"===e?!this.host._furnPalette:!this.host._decorImagePalette)return!1;t.preventDefault();const s=t.pointerType||"mouse";if("mouse"===s)return"furniture"===e?this._furnPlace(this._svgPoint(t),t.shiftKey,s):this._decorImagePlace(this._svgPoint(t),s),!0;const i=this.host._furnTouchPending;return i&&i.pid!==t.pointerId?(i.cancelled=!0,this.host._furnPreviewInput=null,!0):(this.host._furnTouchPending={pid:t.pointerId,sx:t.clientX,sy:t.clientY,pointerType:s,cancelled:!1},this.host._furnPreviewInput=null,ch(t),!0)}if("select"===e&&(this.host._decorSel=null),this.host._bdMovable){const e=this.host._bdRect,s=this._svgPoint(t),i=e.x+e.w/2,o=e.y+e.h/2,a=-Oe(e.angle)*Math.PI/180,n=s[0]-i,r=s[1]-o,l=[i+n*Math.cos(a)-r*Math.sin(a),o+n*Math.sin(a)+r*Math.cos(a)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this.host._decorDraft;if(this.host._decorDraft=null,!t)return;const e=.5*this.host._gridPitch;if(!Be(t.kind,t.a,t.b,e))return;const s=lh,i=this.host._decorH,o=this.host._decorStyle,a=this._geometrySnapshot(),n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r=A;let l;if("line"===t.kind)l={id:n,kind:"line",x1:r(t.a[0]/s),y1:r(t.a[1]/i),x2:r(t.b[0]/s),y2:r(t.b[1]/i),...We(o,!1)};else{const e=r(Math.min(t.a[0],t.b[0])/s),a=r(Math.min(t.a[1],t.b[1])/i),h=Math.abs(t.b[0]-t.a[0])/s,c=Math.abs(t.b[1]-t.a[1])/i;l={id:n,kind:t.kind,x:e,y:a,w:h,h:c,...We(o,!0)}}this.host._curSpaceCfg.decor=[...this.host._decorList,l],this.host._decorSel=n,this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this.host._mode)return;const s=this.host._decorTool;if("text"===s){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==s&&"erase"!==s||(t.stopPropagation(),t.preventDefault(),"erase"!==s?(this.host._decorSel=e.id,this.host._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},ch(t)):this.host._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this.host._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const s=this._svgPoint(t),i=e.orig,o=("line"===i.kind?i.x1:i.x)*lh,a=("line"===i.kind?i.y1:i.y)*this.host._decorH,n=this._decorSnap([o+(s[0]-e.start[0]),a+(s[1]-e.start[1])],t.pointerType,e.id);let r=(n[0]-o)/lh,l=(n[1]-a)/this.host._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=Ht;r=Math.max(-_-c,Math.min(_-c-p,r)),l=Math.max(-_-d,Math.min(_-d-u,l)),(r||l)&&(e.moved=!0);this.host._curSpaceCfg.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s=e.orig;return"line"===t.kind?{...t,x1:s.x1+r,y1:s.y1+l,x2:s.x2+r,y2:s.y2+l}:{...t,x:s.x+r,y:s.y+l}}),this.host.requestUpdate()}_decorShapeDbl(t,e){"decor"===this.host._mode&&"select"===this.host._decorTool&&(t.preventDefault(),t.stopPropagation(),this.host._decorMove=null,this.host._decorSel=e.id,this._openDecorProperties(e))}_openDecorProperties(t){if("decor"!==this.host._mode||"select"!==this.host._decorTool)return;if("text"===t.kind)return void this._decorOpenText(t);if(!["line","rect","ellipse","furniture","image"].includes(t.kind))return;"image"===t.kind&&this._decorImageCatalogLoad();const e=this.host._decorResolvedStyle(t),s="line"===t.kind?t:null,i=this.host._decorBoxOf(t);this.host._decorShapeDialog={id:t.id,kind:t.kind,color:e.color,opacity:e.opacity,widthCm:e.widthCm,angle:this.host._angleField(Oe(s?je([s.x1*lh,s.y1*this.host._decorH],[s.x2*lh,s.y2*this.host._decorH]):t.angle)),...s?{lengthCm:Ee(Math.hypot((s.x2-s.x1)*lh,(s.y2-s.y1)*this.host._decorH),this.host._cellCm,this.host._gridPitch),lineStyle:"dashed"===s.line_style?"dashed":"solid"}:{},...i?{sizeWCm:Ee(i.w,this.host._cellCm,this.host._gridPitch),sizeHCm:Ee(i.h,this.host._cellCm,this.host._gridPitch)}:{},..."furniture"===t.kind?{symbol:t.symbol,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Lt(Ee(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Lt(Ee(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."image"===t.kind?{assetId:t.asset_id,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Lt(Ee(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Lt(Ee(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."rect"===t.kind||"ellipse"===t.kind?{fill:e.fill,fillColor:e.fillColor,fillOpacity:e.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const s=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Dt(t[1])),i=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||i?"":Ct(t.entity,o),n=!(!String(t.entity??"").trim()||s||a&&!i);if(a&&!n){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+a+e.slice(t+2):`${e}${e?" ":""}${a}`}this.host._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:b(t.color,this.host._decorStyle.color),opacity:Ge(t.opacity,this.host._decorStyle.opacity),angle:this.host._angleField(t.angle),sizeCm:this.host._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:n||void 0},this.host._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this.host._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this.host._decorTextDialog;if(!e)return;const s=Ct(e.pickerEntity,t);if(!s)return;const i=e.text,o=Math.max(0,Math.min(i.length,this.host._decorTextSelection.start)),a=Math.max(o,Math.min(i.length,this.host._decorTextSelection.end));if(i.length-(a-o)+s.length>200)return;const n=i.slice(0,o)+s+i.slice(a),r=o+s.length;this.host._decorTextSelection={start:r,end:r},this.host._decorTextDialog={...e,text:n,preserveLegacy:void 0},this.host.updateComplete.then(()=>{const t=this.host.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(r,r))})}_decorSaveText(){const t=this.host._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this.host._decorTextDialog=null);const s=this._geometrySnapshot(),i=this.host._curSpaceCfg,o={color:t.color,opacity:Ge(t.opacity),size_cm:Number(Math.max(.1,Math.min(oh,t.sizeCm)).toFixed(4)),...Oe(t.angle)?{angle:Oe(t.angle)}:{}};if(t.id)i.decor=this.host._decorList.map(s=>{if(s.id!==t.id)return s;if("text"!==s.kind)return s;if(t.preserveLegacy){const{angle:t,size:i,scale:a,...n}=s;return{...n,text:e,...o}}const{entity:i,attr:a,unit:n,...r}=s,{angle:l,size:h,scale:c,...d}=r;return{...d,text:e,...o}});else{const s="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this.host._decorList,{id:s,kind:"text",x:t.x,y:t.y,text:e,...o}],this.host._decorSel=s}this.host._decorTextDialog=null,this._recordGeometry(this.host._t(t.id?"history.decor_edit":"history.decor_add"),s),this._saveConfig(),this.host.requestUpdate()}_decorSaveShape(){const t=this.host._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),s={color:t.color,opacity:Ge(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Ge(t.fillOpacity,.25)},i=this.host._curSpaceCfg,o="furniture"===t.kind||"image"===t.kind?Nt(t.sizeWField,this.host._imperial,Ht*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?Nt(t.sizeHField,this.host._imperial,Ht*this.host._cellCm):null;("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a)&&(i.decor=this.host._decorList.map(e=>{if(e.id!==t.id)return e;const i="rect"===t.kind||"ellipse"===t.kind,n=We(s,i);if("line"===e.kind){const s=(e.x1+e.x2)/2*lh,i=(e.y1+e.y2)/2*this.host._decorH,o=Tt(Math.max(.1,Number(t.lengthCm)||.1),this.host._cellCm,this.host._gridPitch),a=Oe(t.angle)*Math.PI/180,r=Math.cos(a)*o/2,l=Math.sin(a)*o/2,h=this._snap([s-r,i-l]),c=this._snap([s+r,i+l]),{width:d,line_style:p,...u}=e;return{...u,...n,x1:A(h[0]/lh),y1:A(h[1]/this.host._decorH),x2:A(c[0]/lh),y2:A(c[1]/this.host._decorH),..."dashed"===t.lineStyle?{line_style:"dashed"}:{}}}if("furniture"===e.kind){const s=e.w*lh,i=e.h*this.host._decorH,r=Tt(Math.abs(o),this.host._cellCm,this.host._gridPitch),l=Tt(Math.abs(a),this.host._cellCm,this.host._gridPitch),h=e.x*lh+s/2,c=e.y*this.host._decorH+i/2,d=Oe(t.angle),{width:p,angle:u,flip_h:_,flip_v:m,...g}=e;return{...g,...n,x:A((h-r/2)/lh),y:A((c-l/2)/this.host._decorH),w:r/lh,h:l/this.host._decorH,...t.symbol?{symbol:t.symbol}:{},...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...d?{angle:d}:{}}}if("image"===e.kind){const s=e.w*lh,i=e.h*this.host._decorH,n=Tt(Math.abs(o),this.host._cellCm,this.host._gridPitch),r=Tt(Math.abs(a),this.host._cellCm,this.host._gridPitch),l=e.x*lh+s/2,h=e.y*this.host._decorH+i/2,c=Oe(t.angle);return{id:e.id,kind:"image",asset_id:t.assetId||e.asset_id,x:A((l-n/2)/lh),y:A((h-r/2)/this.host._decorH),w:n/lh,h:r/this.host._decorH,opacity:Ge(t.opacity,1),...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...c?{angle:c}:{}}}if("rect"===e.kind||"ellipse"===e.kind){const s=e.w*lh,i=e.h*this.host._decorH,o=Math.max(this.host._gridPitch,oe(Tt(Number(t.sizeWCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),a=Math.max(this.host._gridPitch,oe(Tt(Number(t.sizeHCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),r=e.x*lh+s/2,l=e.y*this.host._decorH+i/2,h=Oe(t.angle),c=qe({x:r-o/2,y:l-a/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...n,x:A(c[0]/lh),y:A(c[1]/this.host._decorH),w:o/lh,h:a/this.host._decorH,...h?{angle:h}:{}}}return e}),"image"!==t.kind&&this._updateDecorStyle({...s,fill:"rect"===t.kind||"ellipse"===t.kind?s.fill:this.host._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?s.fillColor:this.host._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?s.fillOpacity:this.host._decorStyle.fillOpacity}),this.host._decorShapeDialog=null,this._recordGeometry(this.host._t("history.decor_edit"),e),this._saveConfig(),this.host.requestUpdate())}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*lh,(t.y1+t.y2)/2*this.host._decorH]:"furniture"===t.kind||"image"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*lh,(t.y+t.h/2)*this.host._decorH]:[t.x*lh,t.y*this.host._decorH]}_dtApply(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const i={...s};"text"===s.kind&&void 0!==e.textSizeCm&&(delete i.size,delete i.scale);const o={...i};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(oh,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this.host._cfgEpoch++,this.host.requestUpdate())}_dtStart(t,e,s,i){const o=this.host._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[a,n]=this._dtPivot(o),r=this._svgPoint(t),l=this.host._decorBoxOf(o);this.host._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:a,ay:n,r0:Math.hypot(r[0]-a,r[1]-n),a0:180*Math.atan2(r[1]-n,r[0]-a)/Math.PI,textSizeCm0:"text"===o.kind?this.host._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:s?.[0],sgy:s?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:i,moved:!1},ch(t)}_dtMove(t){const e=this.host._dtDrag;if(!e)return;const s=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const i=this._decorSnap(s,t.pointerType,e.id),o=A(i[0]/lh),a=A(i[1]/this.host._decorH),n=e.origShape,r=0===e.lineEnd?n.x1:n.x2,l=0===e.lineEnd?n.y1:n.y2;return(Math.abs(o-r)>1e-9||Math.abs(a-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:a}:{x2:o,y2:a})}if("scale"===e.kind&&e.orig){if("furniture"===e.origShape.kind||"image"===e.origShape.kind){const i=Ue({...e.orig,flip_h:e.origShape.flip_h,flip_v:e.origShape.flip_v},e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,Tt(.1,this.host._cellCm,this.host._gridPitch)),o=Math.abs(i.x-e.orig.x)>1e-9||Math.abs(i.y-e.orig.y)>1e-9||Math.abs(i.w-e.orig.w)>1e-9||Math.abs(i.h-e.orig.h)>1e-9||!!i.flip_h!=!!e.origShape.flip_h||!!i.flip_v!=!!e.origShape.flip_v;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyFurnitureBox(e.id,i)}const i=Ke(e.orig,e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,this.host._gridPitch,this.host._gridPitch),o=Math.abs(i.x-e.orig.x)>1e-6||Math.abs(i.y-e.orig.y)>1e-6||Math.abs(i.w-e.orig.w)>1e-6||Math.abs(i.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,i)}if("scale"===e.kind){const t=Math.hypot(s[0]-e.ax,s[1]-e.ay);if(e.r0<1e-6)return;const i=Math.max(.1,Math.min(oh,e.textSizeCm0*(t/e.r0))),o=Math.abs(i-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:i})}const i=180*Math.atan2(s[1]-e.ay,s[0]-e.ax)/Math.PI;let o;"furniture"===e.origShape.kind||"image"===e.origShape.kind?o=Ve(e.angle0,e.a0,i,t.shiftKey):(o=e.angle0+(i-e.a0),t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360));const a=Math.abs(o-e.angle0)>1e-6;(a||e.moved)&&(e.moved||=a,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this.host._dtDrag;this.host._dtDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.decor_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_dtMeasure(){const t=this.host._dtSel;if(!t)return void(this.host._dtBox&&(this.host._dtBox=null,this.host.requestUpdate()));let e;if("line"===t.kind){const s=t.x1*lh,i=t.y1*this.host._decorH,o=t.x2*lh,a=t.y2*this.host._decorH;e={id:t.id,x:Math.min(s,o),y:Math.min(i,a),w:Math.abs(o-s),h:Math.abs(a-i)}}else if("furniture"===t.kind||"image"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*lh,y:t.y*this.host._decorH,w:t.w*lh,h:t.h*this.host._decorH};else{const s=this.host.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!s||"function"!=typeof s.getBBox)return;let i;try{i=s.getBBox()}catch{return}if(!i||!i.width&&!i.height)return;e={id:t.id,x:i.x,y:i.y,w:i.width,h:i.height}}const s=this.host._dtBox;s&&s.id===e.id&&Math.abs(s.x-e.x)<.01&&Math.abs(s.y-e.y)<.01&&Math.abs(s.w-e.w)<.01&&Math.abs(s.h-e.h)<.01||(this.host._dtBox=e,this.host.requestUpdate())}_deleteDecor(t){if(!this.host._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this.host._curSpaceCfg.decor=this.host._decorList.filter(e=>e.id!==t),this.host._decorSel===t&&(this.host._decorSel=null),this._recordGeometry(this.host._t("history.decor_delete"),e),this._saveConfig(),this.host.requestUpdate()}_decorDeleteSel(){this.host._decorSel&&this._deleteDecor(this.host._decorSel)}_confirmDecorErase(){const t=this.host._decorEraseConfirm;this.host._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}_furnFieldValue(t){return Math.round(100*(this.host._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return Je(this.host._imperial?30.48*t:100*t)}_furnShiftAttach(){this._furnShiftAttached||(this._furnShiftAttached=!0,window.addEventListener("keydown",this._furnShiftListener),window.addEventListener("keyup",this._furnShiftListener))}_furnShiftDetach(){this._furnShiftAttached&&(this._furnShiftAttached=!1,window.removeEventListener("keydown",this._furnShiftListener),window.removeEventListener("keyup",this._furnShiftListener))}_furnPick(t){const e=Ye(t);this.host._furnPalette={symbol:t,w:e.w,h:e.h},this._furnShiftAttach()}get _furnWalls(){return function(t){const e=t._spaceModel();if(!e)return[];const s=[e.id,t._cfgEpoch,t._cellCm,t._gridPitch,t._wallKeyPitch].join("|"),i=Na.get(t);if(i?.key===s)return i.value;const o=[...Aa(e.rooms,t._spaceWalls,t._openCuts(),t._wallKeyPitch,t._cellCm,t._gridPitch,E),...Fa(t._rawPhysicalBodiesR())];return Na.set(t,{key:s,value:o}),o}(this.host)}_resolveFurniturePlacement(t,e=!1,s="mouse"){const i=this.host._furnPalette,o=this.host._curSpaceCfg;if(!i||!o)return null;const a=this.host._decorH,n=this._decorSnap(t,s);return function(t){const{symbol:e,widthCm:s,depthCm:i,point:o,canvasW:a,canvasH:n,cellCm:r,gridPitch:l,walls:h,wallReach:c,free:d=!1,intentPoint:p=o,preferredNormal:u}=t;if(!(z(e)&&n>0&&Number.isFinite(o[0])&&Number.isFinite(o[1])))return null;const _=Number.isFinite(p[0])&&Number.isFinite(p[1])?p:o,m=F(N(s,r,l,a)),g=F(N(i,r,l,a));let f=o[0],b=o[1],v=0;const y=d?null:Ta(f,b,g*n,h,c,l,_,u);return y&&(f=y.cx,b=y.cy,v=y.angle),{symbol:e,x:A(f/a-m/2),y:A(b/n-g/2),w:m,h:g,angle:Number(v.toFixed(2))}}({symbol:i.symbol,widthCm:i.w,depthCm:i.h,point:[n[0],n[1]],canvasW:1e3,canvasH:a,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,walls:this._furnWalls,wallReach:6*this.host._gridPitch,intentPoint:[t[0],t[1]],free:e})}_furniturePreviewPlacement(){const t=this.host._furnPreviewInput;return t&&"decor"===this.host._mode&&"furniture"===this.host._decorTool&&this.host._furnPalette&&this.host._pointerModality.hoverEnabled?this._resolveFurniturePlacement(t.raw,t.free,"mouse"):null}_renderFurniturePlacementPreview(t){const e=this._furniturePreviewPlacement();if(!e)return d;const s=z(e.symbol);if(!s)return d;const i=Ze(e,lh,this.host._decorH,s.viewW,s.viewH),o=this.host._decorStyle,a=Xe(Tt(o.widthCm,this.host._cellCm,this.host._gridPitch),t);return Rt``}_furnLive(){const t=this.host._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this.host._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const s=e.w*lh,i=e.h*this.host._decorH,o=Qe(e.x*lh,e.y*this.host._decorH,s,i,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],n=a(o[0],o[1]),r=a(o[0],o[3]);return[{x:n[0],y:n[1],text:this.host._fmtLen([0,0],[s,0])},{x:r[0],y:r[1],text:this.host._fmtLen([0,0],[0,i])}]}_furnPlace(t,e=!1,s="mouse"){const i=this._resolveFurniturePlacement(t,e,s);if(!i)return;const o=this.host._curSpaceCfg;if(!o)return;const a=this._geometrySnapshot(),n="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r={id:n,kind:"furniture",symbol:i.symbol,x:i.x,y:i.y,w:i.w,h:i.h,...We(this.host._decorStyle,!1)};i.angle&&(r.angle=i.angle),o.decor=[...this.host._decorList,r],this.host._decorSel=n,this.host._decorTool="select",this.host._furnPalette=null,this.host._furnCategory=null,this._furnShiftDetach(),this._clearFurniturePreview(),this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_clearFurniturePreview(){this.host._furnPreviewInput=null,this.host._furnTouchPending=null}_furnPointerMove(t,e){const s=this.host._furnTouchPending;if(s?.pid===t.pointerId)return Math.abs(t.clientX-s.sx)+Math.abs(t.clientY-s.sy)>8&&(s.cancelled=!0),this.host._furnPreviewInput=null,!0;const i="furniture"===this.host._decorTool?!!this.host._furnPalette:"image"===this.host._decorTool&&!!this.host._decorImagePalette;if("decor"!==this.host._mode||!i)return this._clearFurniturePreview(),!1;if("mouse"!==t.pointerType||!e)return this.host._furnPreviewInput=null,!1;const o=this._svgPoint(t);return this.host._furnPreviewInput={raw:[o[0],o[1]],free:t.shiftKey},!0}_furnPointerLeave(t){this.host._furnTouchPending?.pid===t.pointerId&&(this.host._furnTouchPending.cancelled=!0),this.host._furnPreviewInput=null}_furnPointerUp(t){const e=this.host._furnTouchPending;return!(!e||e.pid!==t.pointerId)&&(this.host._furnTouchPending=null,!e.cancelled&&"decor"===this.host._mode&&("furniture"===this.host._decorTool&&this.host._furnPalette||"image"===this.host._decorTool&&this.host._decorImagePalette)&&("furniture"===this.host._decorTool?this._furnPlace(this._svgPoint(t),t.shiftKey,e.pointerType):this._decorImagePlace(this._svgPoint(t),e.pointerType)),!0)}_furnMoveUpdate(t){const e=this.host._decorMove;if("furniture"!==e.orig.kind)return;const s=e.orig,i=this.host._curSpaceCfg;if(!i)return;const o=lh,a=this.host._decorH,n=this._svgPoint(t),r=(s.x+s.w/2)*o+(n[0]-e.start[0]),l=(s.y+s.h/2)*a+(n[1]-e.start[1]);let h,c,d=Number(s.angle)||0;const p=d*Math.PI/180,u=[-Math.sin(p),Math.cos(p)],_=t.shiftKey?null:Ta(r,l,s.h*a,this._furnWalls,6*this.host._gridPitch,this.host._gridPitch,[r,l],u);if(_)h=_.cx/o-s.w/2,c=_.cy/a-s.h/2,d=_.angle;else{const i=this._decorSnap([r-s.w/2*o,l-s.h/2*a],t.pointerType,e.id);h=i[0]/o,c=i[1]/a}h=A(h),c=A(c),(Math.abs(h-s.x)>1e-9||Math.abs(c-s.y)>1e-9||Math.abs(d-(Number(s.angle)||0))>1e-9)&&(e.moved=!0),i.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s={...t,x:h,y:c};return d?s.angle=Number(d.toFixed(2)):delete s.angle,s}),this.host.requestUpdate()}_decorApplyBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=lh,o=this.host._decorH;s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const a=qe(e,s.angle,t=>this._snap(t));return{...s,x:A(a[0]/i),y:A(a[1]/o),w:Math.max(this.host._gridPitch/i,Math.min(2*Ht,e.w/i)),h:Math.max(this.host._gridPitch/o,Math.min(2*Ht,e.h/o))}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorApplyFurnitureBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=lh,o=this.host._decorH,a=Tt(.1,this.host._cellCm,this.host._gridPitch);s.decor=this.host._decorList.map(s=>{if(s.id!==t||"furniture"!==s.kind&&"image"!==s.kind)return s;const{flip_h:n,flip_v:r,...l}=s;return{...l,x:A(e.x/i),y:A(e.y/o),w:Math.max(a/i,Math.min(2*Ht,e.w/i)),h:Math.max(a/o,Math.min(2*Ht,e.h/o)),...e.flip_h?{flip_h:!0}:{},...e.flip_v?{flip_v:!0}:{}}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorImagePlace(t,e="mouse"){this._decorImages.place(t,e)}_renderDecorImagePlacementPreview(){return this._decorImages.renderPlacementPreview()}_renderMissingDecorImage(t,e,s,i,o,a,n,r,l){return this._decorImages.renderMissing(t,e,s,i,o,a,n,r,l)}async _decorImageUpload(t,e=!1){await this._decorImages.uploadFromInput(t,e)}async _uploadDecorImage(t,e,s){await this._decorImages.upload(t,e,s)}async _decorImageDelete(t){await this._decorImages.delete(t)}async _decorImageCatalogLoad(){await this._decorImages.loadCatalog()}_renderDecorImagePalette(){return this._decorImages.renderImagePalette()}_renderFurnPalette(){return this._decorImages.renderFurniturePalette()}_openBackdropDialog(t){if(!this.host._bdMovable||!this.host._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this.host._bdDrag=null;const e=this.host._bdRect;this.host._backdropDialog={widthCm:Ee(e.w,this.host._cellCm,this.host._gridPitch),heightCm:Ee(e.h,this.host._cellCm,this.host._gridPitch),angle:this.host._angleField(e.angle)}}_saveBackdropDialog(){const t=this.host._backdropDialog,e=this.host._bdBase,s=this.host._bdRect;if(!t||!e||!s)return;const i=Oe(t.angle),o=this._geometrySnapshot(),a=Math.min(e.w*xt,Math.max(e.w*kt,oe(Tt(t.widthCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),n=Math.min(e.h*xt,Math.max(e.h*kt,oe(Tt(t.heightCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),r=s.x+s.w/2,l=s.y+s.h/2,h=qe({x:r-a/2,y:l-n/2},i,t=>this._snap(t));this._bdApply((h[0]-e.x)/lh,(h[1]-e.y)/lh,a/e.w,n/e.h,i),this.host._backdropDialog=null,this._recordGeometry(this.host._t("history.backdrop_transform"),o),this._saveConfig()}_bdApply(t,e,s,i,o){const a=this.host._curSpaceCfg;if(!a)return;a.plan_x=Number(A(t).toFixed(6)),a.plan_y=Number(A(e).toFixed(6)),delete a.plan_scale,a.plan_scale_x=Number(Math.min(xt,Math.max(kt,s)).toFixed(6)),a.plan_scale_y=Number(Math.min(xt,Math.max(kt,i)).toFixed(6));const n=Oe(o);n?a.plan_angle=Number(n.toFixed(2)):delete a.plan_angle,this.host._cfgEpoch++,this.host.requestUpdate()}_bdStart(t,e,s=!1){const i=this.host._bdBase,o=this.host._bdRect;if(!i||!o)return!1;const a=this._svgPoint(t),n=e?e[0]:0,r=e?e[1]:0,l=n>0?o.x:o.x+o.w,h=r>0?o.y:o.y+o.h;return this.host._bdDrag={kind:s?"rotate":e?"scale":"move",pid:t.pointerId,sx:a[0],sy:a[1],base:i,p0:this.host._bdParams,fx:l,fy:h,sgx:n,sgy:r,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},ch(t),!0}_bdMove(t){const e=this.host._bdDrag;if(!e)return;const s=this._svgPoint(t),i=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,a=this._snap([t+(s[0]-e.sx),o+(s[1]-e.sy)]),n=Math.abs(a[0]-t)>1e-9||Math.abs(a[1]-o)>1e-9;if(!n&&!e.moved)return;return e.moved||=n,void this._bdApply((a[0]-i.x)/lh,(a[1]-i.y)/lh,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const i=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,a=180*Math.atan2(e.sy-o,e.sx-i)/Math.PI;let n=e.p0.angle+(180*Math.atan2(s[1]-o,s[0]-i)/Math.PI-a);t.shiftKey||(n=5*Math.round(n/5)),n=Oe(n);const r=Math.abs(n-e.p0.angle)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,n)}const o=Ke(e.rect0,e.sgx,e.sgy,s[0],s[1],!t.shiftKey,this.host._gridPitch,Math.min(i.w,i.h)*kt),a=o.w/Math.max(1e-9,i.w),n=o.h/Math.max(1e-9,i.h),r=Math.abs(a-e.p0.sx)>1e-9||Math.abs(n-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!r&&!e.moved)return;e.moved||=r;const l=qe(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-i.x)/lh,(l[1]-i.y)/lh,a,n,e.p0.angle)}_bdReset(){const t=this.host._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this.host._bdDrag=null,this._recordGeometry(this.host._t("history.backdrop_transform"),e),this._saveConfig(),this.host._showToast(this.host._t("decor.backdrop_reset_done")),this.host.requestUpdate()}_bdUp(){const t=this.host._bdDrag;this.host._bdDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.backdrop_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_renderBackdropFrame(t){const e=this.host._bdRect;if(!this.host._bdActive||!e)return d;const s=.02*Math.max(t.w,t.h),i=s/4,o=e.x+e.w/2,a=e.y+e.h/2,n=Oe(e.angle),r=2.2*s;return Rt` - - - {t.stopPropagation(),t.preventDefault(),this._bdStart(t,void 0,!0)}}> - - ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,o,a])=>{const n=t<0?e.x:e.x+e.w,r=o<0?e.y:e.y+e.h;return Rt`{e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} - `}_renderEditorGroupLauncher(t){return this.host._editorSecondary.renderGroupLauncher(t,this.host._editorToolbarGroups,this.host._editorSecondaryCopy)}_runEditorContext(t,e){return this.host._editorSecondary.runContext(t,this.host._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this.host._editorSecondary.renderGroupModel(t,this.host._editorSecondaryContextId,this.host._editorSecondaryCopy)}_renderDrawWallControl(){const t="column"===this.host._tool?1:0;return p``}_renderPlanSecondary(){const t=this.host._editorSecondaryContextId,e=this.host._physicalSel;if(e){if(!("partition"===e.kind?!!this.host._curSpaceCfg.partitions?.some(t=>t.id===e.id):"column"===e.kind?!!this.host._curSpaceCfg.wall_columns?.some(t=>t.id===e.id):!!this.host._curSpaceCfg.room_drafts?.some(t=>t.id===e.id)))return null;const s="partition"===e.kind?this.host._t("markup.partition"):"column"===e.kind?this.host._t("markup.column"):this.host._t("markup.add");return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` - - `}}const s="draw"===this.host._tool||"column"===this.host._tool,i="column"===this.host._tool?"markup.hint_column":"resize"===this.host._tool?"markup.hint_resize":"wallthick"===this.host._tool?"markup.hint_wallthick":null,o="draw"===this.host._tool?this.host._t(this.host._path.length?"markup.hint_points":"markup.hint_start",this.host._path.length?{n:this.host._path.length}:void 0):"";if(!s&&!i&&!o)return null;const a="draw"===this.host._tool&&this.host._path.length>0;return{contextId:t,kind:a?"operation":"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("draw"===this.host._tool?"markup.add":"column"===this.host._tool?"markup.column":"resize"===this.host._tool?"markup.resize":"markup.wallthick")}),content:p` - ${s?this._renderDrawWallControl():d} - ${o?p`${o}`:d} - ${i?p`${this.host._t(i)}`:d} - ${"draw"===this.host._tool&&this.host._path.length?p``:d}`}}_renderDecorSecondary(){const t=this.host._editorSecondaryContextId;if("furniture"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.furniture")}),launcherId:"furniture",dismissPolicy:this.host._furnPalette?"stay-open-on-canvas":"outside",dismiss:()=>{"furniture"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._furnPalette=null,this._furnShiftDetach(),this.host._furnCategory=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderFurnPalette()};if("image"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.image")}),launcherId:"image",dismissPolicy:this.host._decorImagePalette?"stay-open-on-canvas":"outside",dismiss:()=>{"image"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._decorImagePalette=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderDecorImagePalette()};const e=this.host._dtSel;if(e){const s=this.host._t(`decor.${e.kind}`);return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` - - `}}const s="line"===this.host._decorTool||"rect"===this.host._decorTool||"ellipse"===this.host._decorTool,i="rect"===this.host._decorTool||"ellipse"===this.host._decorTool,o="backdrop"===this.host._decorTool&&!!this.host._bdRect;return s||o?{contextId:t,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t(s?`decor.${this.host._decorTool}`:"decor.backdrop")}),content:p` - ${s?p` - this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> - - ${i?p` - this._updateDecorStyle({...this.host._decorStyle,fillColor:t.detail.color,fillOpacity:t.detail.opacity})}>`:d} - `:d} - ${o?p`${this.host._t("decor.backdrop_hint")}`:d}`}:null}_withBackdropReset(t){if(!this.host._bdMoved||this.host._bdDrag||"palette"===t?.kind)return t;const e=this.host._editorSecondaryContextId,s=p``;return t?{...t,kind:"selection"===t.kind?"mixed":t.kind,content:p`${t.content}${s}`}:{contextId:e,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("decor.backdrop")}),content:s}}_renderEditorSecondary(){if(!this.host._editing)return d;const t=this.host._editorSecondary.activeGroup(this.host._editorToolbarGroups),e=t?this._renderEditorGroupModel(t):"plan"===this.host._mode?this._renderPlanSecondary():"decor"===this.host._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this.host._editorSecondary.render(e,this.host._editorSecondaryDialogBlocked)}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this.host._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],...this.host._haDecorAssetsApi===ts?[["image","mdi:image-plus-outline","decor.image"]]:[],["erase","mdi:eraser","decor.erase"]],e=this.host._geometryHistory.undoName,s=this.host._geometryHistory.redoName;return p`
    -
    - ${t.map(([t,e,s])=>p``)} - ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} - this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> - - - -
    -
    - -
    -
    `}_renderDecorEraseConfirm(){const t=this.host._decorEraseConfirm,e=this.host._t(`decor.${t.kind}`);return p`this.host._decorEraseConfirm=null}> -

    ${this.host._t("confirm.erase_decor",{kind:e})}

    -
    - - - -
    -
    `}_renderDecorTextDialog(){const t=this.host._decorTextDialog,e=(t.pickerEntity||"").trim(),s=e?this.host.hass?.states?.[e]:null;return p`this.host._decorTextDialog=null}> -
    - - ${""} - - this.host._decorTextDialog={...t,...e.detail}}> - -
    this.host._decorTextDialog={...t,sizeCm:this.host._decorTextCm(Number(e.target.value))}} /> - ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    - - this.host._decorTextDialog={...t,angle:e.target.value}} /> - - - this.host._decorTextDialog={...t,pickerEntity:e.target.value}} /> - - ${Object.keys(this.host.hass?.states||{}).map(t=>p``)} - - ${e?p` - - - `:d} -
    -
    - - - -
    -
    `}_renderDecorShapeDialog(){return this._decorImages.renderShapeDialog()}_renderBackdropDialog(){const t=this.host._backdropDialog,e=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`this.host._backdropDialog=null}> -
    - -
    this.host._backdropDialog={...t,widthCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ×this.host._backdropDialog={...t,heightCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ${e}
    - - this.host._backdropDialog={...t,angle:e.target.value}} /> -
    -
    - - -
    -
    `}_cssPxToRender(t){const e=this.host._stageEl,s=this.host._viewOr(this.host._baseVb());return e?.clientWidth&&e.clientHeight?Math.max(s.w/e.clientWidth,s.h/e.clientHeight)*t:this.host._gridPitch/8*t}_deleteRoomClick(t){const e=this.host._spaceModel();if(!e)return;const s=[...e.rooms].reverse().find(e=>this._pointInRoom(t,e));s?s.id&&(this.host._roomDeleteDialog={roomId:s.id,name:s.name}):this.host._showToast(this.host._t("toast.delete_room_pick"))}_wallThickHit(t){const e=this.host._spaceModel();if(!e)return null;const s=6*this.host._gridPitch,i=this.host._openCuts();let o=null;const a=(t,e,i)=>{e>s||(!o||e100)return void this._showPhysicalRange(100);if("room"!==e.source.kind){const t=this._geometrySnapshot();if("partition"===e.source.kind){const t=(s.partitions||[]).find(t=>t.id===e.source.id);if(!t)return;if(0===n&&Gt(s.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));t.cm=n}else{const t=(s.room_drafts||[]).find(t=>t.id===e.source.id),i=t?.segments?.[e.source.segment];if(!i)return;i.cm=n}this.host._wallDialog=null;return this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),t)&&this.host._showToast(this.host._t("toast.wallthick_set")),void this.host.requestUpdate()}const r=this._geometrySnapshot(),l=n>0?n:null;if(0===n){const i=.02*this.host._gridPitch,o=[e.a[0],e.a[1],e.b[0],e.b[1]],a=(s.rooms||[]).find(t=>t.id===e.roomId),n=new Set(t&&a&&Array.isArray(a.wall_ids)?a.wall_ids:(s.wall_segments||[]).filter(t=>{const e=[Number(t.a?.[0])*lh,Number(t.a?.[1])*lh],s=[Number(t.b?.[0])*lh,Number(t.b?.[1])*lh];return G(e,o)<=i&&G(s,o)<=i}).map(t=>String(t.id))),r=(s.openings||[]).some(t=>{if("partition"===t.host?.kind)return!1;if("wall"===t.host?.kind&&n.has(t.host.id))return!0;if(t.host)return!1;const e=[Number(t.x)*lh,Number(t.y)*lh];return e.every(Number.isFinite)&&G(e,o)<=i});if(r)return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"))}let h,c=this.host._openCuts();if(n>0&&c.length){const o=[];if(t&&e.roomId){const t=(s.rooms||[]).find(t=>t.id===e.roomId),a=new Set(Array.isArray(t?.wall_ids)?t.wall_ids:[]);for(const t of s.wall_segments||[])a.has(t.id)&&o.push([Number(t.a?.[0])*lh,Number(t.a?.[1])*lh,Number(t.b?.[0])*lh,Number(t.b?.[1])*lh]);if(!o.length){const t=i.rooms.find(t=>t.id===e.roomId),s=t?V(t):null;for(let t=0;s&&t!o.some(e=>G([t[0],t[1]],e)<=a&&G([t[2],t[3]],e)<=a&&G([e[0],e[1]],t)<=a&&G([e[2],e[3]],t)<=a))}h=t&&e.roomId?es(s.walls,i.rooms,e.roomId,l,this.host._wallKeyPitch,c,lh):et(s.walls,e.a,e.b,l,this.host._wallKeyPitch,lh),h=this.host._normalizeWalls(h,c);for(const t of s.wall_segments||[])t.cm=mt(h,t.a,t.b,U,1);h.length?s.walls=h:delete s.walls,this.host._wallDialog=null,this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),r)&&this.host._showToast(this.host._t(0===n?"toast.wallthick_cleared":"toast.wallthick_set")),this.host.requestUpdate()}_wallHatchDefs(t){if(!this.host._spaceWalls.length&&!this.host._physicalBodiesR().length&&!this.host._markup)return Rt``;const e=ss(this.host._cellCm);return Rt` - - - - `}_renderWallThickUi(){const t=this.host._wallThickHover;return t&&t.d?Rt``:Rt``}_renderWallThickDialog(){const t=this.host._wallDialog;return t?p`
    t.stopPropagation()}> -
    - - {this.host._wallDialog={...t,value:e.target.value}}} - @keydown=${t=>{"Enter"===t.key&&(t.preventDefault(),this._wallThickApply(!1))}} /> - ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")} -
    -
    - ${"room"===t.source.kind?p``:d} - - -
    -
    `:p``}_openingAt(t){if(!this.host._openingsR.length)return null;const e=this.host._openingsR.flatMap(e=>{const s=e.angle*Math.PI/180,i=t[0]-e.rx,o=t[1]-e.ry,a=i*Math.cos(s)+o*Math.sin(s);return Math.abs(a)>e.rlen/2+is(12,this.host._cellCm)?[]:[{o:e,localY:-i*Math.sin(s)+o*Math.cos(s)}]});if(!e.length)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i).value;return e.find(({o:t,localY:e})=>{const s="gate"===t.type?!t.flip_v:t.flip_v,i=t.partitionHost||this.host._spaceWalls.length||"gate"===t.type?this.host._openingFace(t,o,!!s):{ox:0,oy:0,cm:0,side:-1},a=os({type:t.type,length:t.rlen,angle:t.angle,amount:this.host._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:i});return Math.abs(e)<=a.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this.host._openingPreset;if("opening"!==this.host._tool||!e)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i),a=`${o.key}|partitions:${this.host._cfgEpoch}`;this.host._openingPlacementIntervalsCache&&this.host._openingPlacementIntervalsCache.key===a||(this.host._openingPlacementIntervalsCache={key:a,value:[...Z(s.rooms,this.host._spaceWalls,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh),...as(s.partitions,this.host._cellCm,this.host._gridPitch)]}),this.host._openingDimensionContextCache&&this.host._openingDimensionContextCache.key===a||(this.host._openingDimensionContextCache={key:a,value:Kl({rooms:s.rooms,walls:this.host._spaceWalls,openCuts:i,partitions:s.partitions,roomOpenings:this.host._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(s),pitch:this.host._wallKeyPitch,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,coordScale:lh,epsilon:2e-4*this.host._gridPitch})});const n=ns({pointer:[t[0],t[1]],preset:e,geometryRevision:this.host._cfgEpoch,renderedLength:this.host._cmToUnits(e.lengthCm),intervals:this.host._openingPlacementIntervalsCache.value,baseTolerance:1.5*this.host._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this.host._pointerModality.modality||"pen"===this.host._pointerModality.modality?10:6),gridStep:this.host._gridPitch});this.host._openingJambBlockCm=n.jambBlockedTarget?n.jambBlockedTarget.physicalHalfWidth/this.host._gridPitch*this.host._cellCm:null;const r=n.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let h;if(r.host){const t=J({id:"preview",type:r.type,x:r.x/lh,y:r.y/lh,angle:r.angle,length:r.renderedLength/lh,host:r.host},s.partitions,lh,this.host._cellCm,this.host._gridPitch).resolved;h=t?rs(t,l):{ox:0,oy:0,cm:0,side:-1}}else h=ls(o.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const c="mi"===this.host.hass?.config?.unit_system?.length,d=th(r,this.host._openingDimensionContextCache.value).map(t=>({x:t.label[0],y:t.label[1],text:Se(t.distance/this.host._gridPitch*this.host._cellCm,c),dimension:t}));return{...r,face:h,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(t){this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingPreset=hs(t,++this.host._openingPresetRevision),this.host._openingHoverCandidate=null,this.host._cursorPt=null)}_clearOpeningPlacement(t){this.host._openingHoverCandidate=null,this.host._openingJambBlockCm=null,t&&(this.host._openingPreset=null,this.host._openingRebindId=null)}_openingClick(t){const e=this.host._spaceModel();if(!e)return;const s=this.host._openingRebindId?null:this._openingAt(t);if(s)return void this._editOpening(s);const i=this.host._openingPreset;if(!i)return;const o=this.host._openingHoverCandidate,a=o&&cs(o,[t[0],t[1]],i.revision,this.host._cfgEpoch)?o:this._resolveOpeningPlacement(t);if(!a){if(null!=this.host._openingJambBlockCm)return void this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Se(this.host._openingJambBlockCm,this.host._imperial)}));const s=1.5*this.host._gridPitch,i=q(t,e.rooms,s);return i&&ds(i.x,i.y,i.angle,this.host._openCuts(),s)?void this.host._showToast(this.host._t("toast.opening_on_zero_wall")):void this.host._showToast(this.host._t("toast.opening_no_wall"))}const n=this.host._openingRebindId?this.host._curSpaceCfg?.openings?.find(t=>t.id===this.host._openingRebindId):null;this.host._openingDialog={...n?{id:n.id,type:n.type,lengthCm:Math.round(n.length*lh/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:n.contact||"",lock:n.lock||"",invert:!!n.invert,flipH:!!n.flip_h,flipV:!!n.flip_v}:{type:a.type,lengthCm:a.lengthCm,contact:"",lock:"",invert:!1,flipH:a.flipH,flipV:a.flipV},...a.host?{host:a.host}:{},x:a.x,y:a.y,angle:a.angle},this.host._openingRebindId=null,this.host._openingHoverCandidate=null,this.host._cursorPt=null}_editOpening(t){this.host._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,..."partition"===t.host?.kind?{host:{...t.host}}:{},x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this.host._mode&&this.host._spaceModel()&&"resize"!==this.host._tool){t.preventDefault(),t.stopPropagation();try{ch(t)}catch{}this.host._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this.host._opDrag.sx)+Math.abs(t.clientY-this.host._opDrag.sy)<=3)return;const s=this.host._spaceModel();if(!s)return;const i=this._svgPoint(t),o=this.host._curSpaceCfg,a=o?.openings?.find(t=>t.id===e.id);if(!a)return;if("partition"===a.host?.kind){const t=s.partitions.find(t=>t.id===a.host.id);if(!t)return;const e=t.b[0]-t.a[0],o=t.b[1]-t.a[1],n=Math.hypot(e,o);if(!(n>1e-9))return;const r=e/n,l=o/n;if(Math.abs((i[0]-t.a[0])*l-(i[1]-t.a[1])*r)>4*this.host._gridPitch)return;const h=a.length*lh/2+tt(t,this.host._cellCm,this.host._gridPitch);let c=(i[0]-t.a[0])*r+(i[1]-t.a[1])*l;if(c=Math.round(c/this.host._gridPitch)*this.host._gridPitch,c=Math.max(h,Math.min(n-h,c)),n<2*h-1e-9)return;const d=c/n,p=(t.a[0]+r*c)/lh,u=(t.a[1]+l*c)/this.host._spaceH;let _=180*Math.atan2(o,e)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=a.host.t!==d||a.x!==p||a.y!==u||a.angle!==_;return this.host._opDrag.moved=!0,m&&(this.host._opDrag.dirty=!0),a.host={...a.host,t:d},a.x=p,a.y=u,a.angle=_,this.host._opMeasure=null,m&&this.host._cfgEpoch++,void this.host.requestUpdate()}const n=q(i,s.rooms,4*this.host._gridPitch);if(!n)return;this.host._opDrag.moved=!0;const r=this._opRuler(n,a.length*lh);this.host._opMeasure=r.measure;const l=r.x/lh,h=r.y/this.host._spaceH,c=a.x!==l||a.y!==h||a.angle!==n.angle;if(c&&(this.host._opDrag.dirty=!0),a.x=l,a.y=h,a.angle=n.angle,Number(this.host._serverCfg?.model_version||0)>=9){const t=_l(a,o?.wall_segments||[]);t?a.host=t:delete a.host}c&&this.host._cfgEpoch++,this.host.requestUpdate()}_opRuler(t,e){const s=this.host._spaceModel()?.rooms||[],i=this.host._gridPitch/2;let o=t.x,a=t.y,n=ps([o,a],t.angle,e,s,i);if(n&&n.centered&&(o!==n.wallCenter[0]||a!==n.wallCenter[1]))[o,a]=n.wallCenter,n=ps([o,a],t.angle,e,s,i);else if(n){const[r,l]=n.wallA,[h,c]=n.wallB,d=h-r,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this.host._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-r)*d+(a-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=r+_/u*d,a=l+_/u*p,n=ps([o,a],t.angle,e,s,i)||n}}if(!n)return{x:o,y:a,angle:t.angle,measure:null};const r="mi"===this.host.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Se(t/this.host._gridPitch*this.host._cellCm,r)});return{x:o,y:a,angle:t.angle,measure:{labels:[l(n.sideA,n.midA),l(n.sideB,n.midB)],guide:n.centered?{x:n.wallCenter[0],y:n.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;const s=this.host._opDrag,i=s.moved;this.host._opMeasure=null,i&&s.dirty&&this._commitPhysicalGeometry(this.host._t("history.move_opening"),s.before),i?window.setTimeout(()=>this.host._opDrag=null,0):this.host._opDrag=null}_opClick(t,e){"plan"===this.host._mode&&"resize"===this.host._tool||(t.stopPropagation(),this.host._opDrag?.moved||"plan"===this.host._mode&&this._editOpening(e))}_saveOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=this._geometrySnapshot(),o=this.host._spaceH,a=(e.openings||[]).find(e=>e.id===t.id),n={...a||{},id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/lh,y:t.y/o,angle:t.angle,length:a&&!t.lengthTouched?a.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/lh,...t.host?{host:{...t.host}}:{}};if("partition"===n.host?.kind){const t=us(a,n)?it(n,s.partitions,lh,this.host._cellCm,this.host._gridPitch):J(n,s.partitions,lh,this.host._cellCm,this.host._gridPitch);if(!t.resolved){const e=s.partitions.find(t=>t.id===n.host.id);if("does-not-fit-jamb"===t.reason&&e){const t=tt(e,this.host._cellCm,this.host._gridPitch);this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Se(t/this.host._gridPitch*this.host._cellCm,this.host._imperial)}))}else this.host._showToast(this.host._t("opening.partition_orphan"));return}const i=(e.openings||[]).flatMap(t=>{if(!t.host||t.id===n.id)return[];const e=J(t,s.partitions,lh,this.host._cellCm,this.host._gridPitch).resolved;return e?[e]:[]});if(_s(t.resolved,i))return void this.host._showToast(this.host._t("toast.opening_no_wall"));Object.assign(n,Y(n,t.resolved,lh))}else if(Number(this.host._serverCfg?.model_version||0)>=9){const t=_l(n,e.wall_segments||[]);if(!t)return void this.host._showToast(this.host._t("toast.opening_no_wall"));n.host=t}else delete n.host;"passage"===t.type?(delete n.contact,delete n.lock,delete n.invert,delete n.flip_h,delete n.flip_v):(n.contact=t.contact||null,n.lock=("door"===t.type||"gate"===t.type)&&t.lock||null,n.invert=t.invert||void 0,n.flip_h="gate"!==t.type&&t.flipH||void 0,n.flip_v=t.flipV||void 0),e.openings=e.openings||[];const r=e.openings.findIndex(t=>t.id===n.id);r>=0?e.openings[r]=n:e.openings.push(n),this._commitPhysicalGeometry(this.host._t(t.id?"history.edit_opening":"history.add_opening"),i),this.host._openingDialog=null,this.host.requestUpdate()}_deleteOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg;if(!t?.id||!e?.openings)return;const s=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._commitPhysicalGeometry(this.host._t("history.delete_opening"),s),this.host._openingDialog=null,this.host.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.host.hass.states)){if(!this.host._openingEntityAvailable(e))continue;const s=e.split(".")[0];if("binary_sensor"!==s&&"cover"!==s)continue;const i=this.host.hass.states[e],o=["door","window","opening","garage_door","garage"].includes(i?.attributes?.device_class||"");("cover"!==s||o)&&t.push([e,i?.attributes?.friendly_name||e,o?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.host.hass.states).filter(t=>t.startsWith("lock.")&&this.host._openingEntityAvailable(t)).map(t=>({value:t,label:this.host.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_toggleOpeningEntityPicker(t){const e=this.host._openingDialog;if(!e)return;const s="contact"===t?!e.contactOpen:!e.lockOpen;this.host._openingDialog={...e,contactOpen:"contact"===t&&s,lockOpen:"lock"===t&&s}}_filterOpeningEntities(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contactFilter:e}:{...s,lockFilter:e})}_selectOpeningEntity(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contact:e,contactOpen:!1,contactFilter:""}:{...s,lock:e,lockOpen:!1,lockFilter:""})}_mergeClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms,i=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const o=i.id;if(!this.host._mergeSel||this.host._mergeSel===o)return void(this.host._mergeSel=this.host._mergeSel===o?null:o);const a=s.find(t=>t.id===this.host._mergeSel),n=a?V(a):null,r=V(i),l=n&&r?ms(n,r):null;if(!l)return this.host._showToast(this.host._t("toast.merge_not_adjacent")),void(this.host._mergeSel=null);this.host._mergeDialog={aId:this.host._mergeSel,bId:o,poly:l,pick:"a"},this.host._mergeSel=null}_commitMerge(){const t=this.host._mergeDialog,e=this.host._curSpaceCfg;if(!t||!e||!this.host._spaceModel())return;const s=this._geometrySnapshot(),i=this.host._spaceH,o="a"===t.pick?t.aId:t.bId,a="a"===t.pick?t.bId:t.aId,n=e.rooms.find(t=>t.id===o);if(!n)return void(this.host._mergeDialog=null);n.poly=t.poly.map(t=>[t[0]/lh,t[1]/i]),delete n.x,delete n.y,delete n.w,delete n.h,e.rooms=e.rooms.filter(t=>t.id!==a);const r=this._commitPhysicalGeometry(this.host._t("history.merge_rooms"),s);this.host._mergeDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),r&&this.host._showToast(this.host._t("toast.rooms_merged",{name:n.name||""}))}_splitClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms;if(!this.host._splitSel){const e=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this.host._splitSel={roomId:e.id,pts:[]})}const i=s.find(t=>t.id===this.host._splitSel.roomId),o=i?V(i):null;if(!i||!o)return void(this.host._splitSel=null);const a=.02*this.host._gridPitch,n=6*this.host._gridPitch,r=gs(t,o),l=r&&fs(r,o,this.host._gridPitch)||r,h=r&&l&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?l:null,c=!!h&&bs(h,o,a),d=this.host._splitSel.pts;if(!d.length)return c?void(this.host._splitSel={...this.host._splitSel,pts:[h]}):void this.host._showToast(this.host._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return ge(e,o,a)?void(this.host._splitSel={...this.host._splitSel,pts:[...d,e]}):void this.host._showToast(this.host._t("toast.split_pick_inside"))}const p=be(o,[...d,h],a);if(!p)return void this.host._showToast(this.host._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=R(u)>=R(_)?u:_,g=m===u?_:u;this.host._pendingSplit={roomId:i.id,mainPoly:m,newPoly:g},this.host._cursorPt=null,this.host._nameSel="",this.host._areaSel="",this.host._roomDialog=!0}_markupMove(t){if(!this.host._markup)return;if("column"===this.host._tool)return void(this.host._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this.host._tool||"wallthick"===this.host._tool)return void(this.host._cursorPt=this._svgPoint(t));const e="draw"===this.host._tool&&!this.host._contourClosed,s="split"===this.host._tool&&!!this.host._splitSel?.pts?.length;if(!e&&!s)return;const i=this._svgPoint(t);if(e){const e=this._resolvePlanDrawPoint(i,t.shiftKey);return this.host._planSnapHover={contextKey:e.contextKey,candidate:e.candidate,conflicts:e.conflicts},this._syncPlanSnapConflictMarkers(e.conflicts),this.host._path.length?void(this.host._cursorPt=e.point):void this._syncPlanSnapActiveMarker(e.candidate)}this.host._cursorPt=this._snap(i)}_saveRoom(){(this.host._areaSel||this.host._nameSel.trim())&&(this.host._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(t){const e=this.host._wallFaceBatch;if(!e)return;const s=e.candidates[e.index];if(!s)return;if(!t&&(s.existing||s.repair)&&1===e.candidates.length)return this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate();const i=t?{candidate:s,create:!0,name:this.host._nameSel.trim(),area:this.host._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:s,create:!1},o=[...e.decisions,i],a=e.index+1;if(a"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,o=.02*this.host._gridPitch,a=this.host._spaceModel();if(!a)return X;for(const e of this._planSnapGeometrySnapshot().value.segments){if("room"===e.sourceKind||G(t,[e.a[0],e.a[1],e.b[0],e.b[1]])>o)continue;if("partition"===e.sourceKind)return i(a.partitions.find(t=>t.id===e.sourceId)?.cm)??X;const s=e.sourceId.lastIndexOf(":"),n=s>=0?e.sourceId.slice(0,s):e.sourceId,r=s>=0?Number(e.sourceId.slice(s+1)):-1;return i(a.room_drafts.find(t=>t.id===n)?.segments[r]?.cm)??X}const n=xn(Math.max(0,e.length-1),s,this.host._drawWallCm,X);for(let s=0;s+1[...t]);if(!e?.sourceKey.startsWith("active:"))return s;const i=/:(\d+)$/.exec(e.sourceKey),o=i?Number(i[1])+("b"===e.endpoint?1:0):-1;return o>=0&&oe.key===t.sourceKey),a=i.find(e=>e.key===t.targetSourceKey);if(!o||!a)return!1;const n="a"===t.endpoint?o.a:o.b;return!(Math.hypot(n[0]-t.from[0],n[1]-t.from[1])>s)&&(!(G(t.to,[a.a[0],a.a[1],a.b[0],a.b[1]])>s)&&!function(t,e){if(!t.sourceKey.startsWith("static:partition|"))return!1;const s=t.sourceKey.slice(17).split("|")[0];return!!s&&e.some(t=>"partition"===t.host?.kind&&t.host.id===s)}(t,this.host._curSpaceCfg?.openings||[]))}_applyWallRepair(t,e){const s=this.host._curSpaceCfg;if(!s||!this._validateWallRepair(t,e.activePath))return!1;const i=e=>{e[0]=t.to[0]/lh,e[1]=t.to[1]/lh};if(t.sourceKey.startsWith("active:")){const o=/:(\d+)$/.exec(t.sourceKey),a=o?Number(o[1])+("b"===t.endpoint?1:0):-1;if(a<0||a>=e.activePath.length)return!1;e.activePath[a]=[...t.to];const n=(s.room_drafts||[]).find(t=>t.id===e.activeDraftId);return n?.points?.[a]&&i(n.points[a]),!0}if(!t.sourceKey.startsWith("static:"))return!1;const[o,a]=t.sourceKey.slice(7).split("|");if("partition"===o){const e=(s.partitions||[]).find(t=>t.id===a);if(!e)return!1;const o=[e.a[0]*lh,e.a[1]*lh],n=[e.b[0]*lh,e.b[1]*lh],r=Math.hypot(o[0]-t.from[0],o[1]-t.from[1])<=2e-4*this.host._gridPitch?e.a:Math.hypot(n[0]-t.from[0],n[1]-t.from[1])<=2e-4*this.host._gridPitch?e.b:null;return!!r&&(i(r),!0)}if("draft"===o){const e=a.lastIndexOf(":"),o=e>=0?a.slice(0,e):"",n=e>=0?Number(a.slice(e+1)):-1,r=(s.room_drafts||[]).find(t=>t.id===o);if(!r?.points?.[n]||!r?.points?.[n+1])return!1;const l=[n,n+1].find(e=>Math.hypot(r.points[e][0]*lh-t.from[0],r.points[e][1]*lh-t.from[1])<=2e-4*this.host._gridPitch);return null!=l&&(i(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const t=this.host._wallFaceBatch,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=(t,e)=>{this.host._showToast(this.host._t(t,e)),this._roomDialogCancel()},o=t.decisions.filter(t=>t.create),a=o.map(t=>t.candidate.ring),n=s.rooms;for(let t=0;ts.id!==e&&!!V(s)&&I(a[t],V(s)));if(s)return void i("toast.room_overlap",{name:s.name||""})}if((e.rooms||[]).length+o.length>400)return void i("toast.physical_limit");if(o.some(t=>t.candidate.split&&!e.rooms.some(e=>e.id===t.candidate.split.roomId)))return void i("toast.contour_cannot_close");const r=o.map(t=>t.candidate.repair).filter(t=>!!t);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],t.activePath))return void i("toast.wall_repair_changed");const l=this._activePathWithRepair(t.activePath,r[0]),h=2e-4*this.host._gridPitch,c=new Map;for(let e=0;et.candidate.atomKeys)),e=o.some(t=>t.candidate.consumeAllActive);for(const s of Un(this._wallGraphSources(l),h)){const i=s.sourceKeys.find(t=>c.has(t));!i||e||t.has(s.key)||d.push({a:s.a,b:s.b,cm:c.get(i)})}}else d.push(...Sn(l,xn(l.length-1,t.activeCms,this.host._drawWallCm,X)));if((e.partitions||[]).length+d.length>hh)return void i("toast.physical_limit");const p=o.map(t=>t.candidate.ring.map(()=>"")),u=t.activeDraftId?(e.room_drafts||[]).find(e=>e.id===t.activeDraftId):null,_=this._draftSegmentsForPath(t.activePath,u,t.activeCms),m=(t,e)=>{const s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`;return s<=i?`${s}|${i}`:`${i}|${s}`};for(let e=0;e<_.length;e++){const s=_[e]?.id;if(!s)continue;const i=t.activePath[e],a=t.activePath[e+1],n=[i[0],i[1],a[0],a[1]],r=[];o.forEach((t,e)=>t.candidate.ring.forEach((s,i)=>{const o=t.candidate.ring[(i+1)%t.candidate.ring.length];G(s,n)<=h&&G(o,n)<=h&&r.push({kind:"room",owner:e,edge:i,a:s,b:o})})),d.forEach((t,e)=>{G(t.a,n)<=h&&G(t.b,n)<=h&&r.push({kind:"partition",owner:e,edge:0,a:t.a,b:t.b})});const l=[(i[0]+a[0])/2,(i[1]+a[1])/2];r.sort((t,e)=>{const s=[t.a[0],t.a[1],t.b[0],t.b[1]],o=[e.a[0],e.a[1],e.b[0],e.b[1]];return(G(l,s)<=h?0:1)-(G(l,o)<=h?0:1)||(G(i,s)<=h?0:1)-(G(i,o)<=h?0:1)||m(t.a,t.b).localeCompare(m(e.a,e.b))});const c=r[0];if(!c)continue;const u=m(c.a,c.b);for(const t of r)m(t.a,t.b)===u&&("room"===t.kind?p[t.owner][t.edge]=s:d[t.owner].id=s)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],t))return void i("toast.wall_repair_changed");const f=o.some(t=>!!t.candidate.split),b=f?Ut(s.rooms,e.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh):null,v=f&&Array.isArray(e.walls)?e.walls:null;for(const t of o){const s=t.candidate.split;if(!s)continue;const o=e.rooms.find(t=>t.id===s.roomId);if(!o)return void i("toast.contour_cannot_close");o.poly=s.mainPoly.map(t=>[t[0]/lh,t[1]/this.host._spaceH]),delete o.x,delete o.y,delete o.w,delete o.h}const y=Date.now().toString(36),$=[];if(o.forEach((t,s)=>{const i=t.area?this.host.hass.areas[t.area]?.name:"",o={id:`r${y}-${s}`,name:t.name||i||this.host._t("room.default_name"),area:t.area||null,poly:t.candidate.ring.map(t=>[t[0]/lh,t[1]/this.host._spaceH]),...p[s].some(Boolean)?{wall_ids:p[s]}:{},...t.settings?{settings:JSON.parse(JSON.stringify(t.settings))}:{}};e.rooms.push(o),$.push({room:o,decision:t})}),t.activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==t.activeDraftId),e.room_drafts.length||delete e.room_drafts),d.length&&(e.partitions||=[],d.forEach((t,s)=>e.partitions.push({id:t.id||`partition-${y}-${s}`,a:[t.a[0]/lh,t.a[1]/lh],b:[t.b[0]/lh,t.b[1]/lh],cm:t.cm}))),f){const t=this.host._normalizeWalls(b,this.host._openCuts());t.length?e.walls=t:v?.length?e.walls=v:delete e.walls}if($.length){this.host._cfgEpoch++;const s=this.host._openCuts(),i=this.host._spaceModel();if(!i)return;let o=e.walls;for(const{room:e,decision:a}of $){const n=a.candidate.ring,r=this._wallSourceCmAt(n[0],t.activePath,t.activeCms);o=vs(o,i.rooms,e.id,r,this.host._wallKeyPitch,s,lh);for(const a of Z(i.rooms,o,s,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh)){if(a.roomId!==e.id||"outer"!==a.kind)continue;const s=[(a.a[0]+a.b[0])/2,(a.a[1]+a.b[1])/2];o=et(o,a.a,a.b,this._wallSourceCmAt(s,t.activePath,t.activeCms),this.host._wallKeyPitch,lh)}}const a=this.host._normalizeWalls(o,s);a.length?e.walls=a:delete e.walls}if(!this._commitPhysicalGeometry(this.host._t("history.wall_face_batch"),g))return;delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices();const w=new Set(o.map(t=>t.area).filter(t=>!!t));if(w.size){const t={...this.host._layout};for(const e of this.host._devices){if(!w.has(e.area||"")||e.space!==this.host._space)continue;if(this.host._layout[e.id])continue;const s=this.host._defPos[e.id];s&&(t[e.id]={s:this.host._space,x:s.x/lh,y:s.y/lh},this.host._dirtyPos.add(e.id))}this.host._layout=t,this.host._persistLayout()}this.host._showToast(this.host._t(o.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:o.length}))}_commitRoom(){const t=this.host._curSpaceCfg,e=this.host._spaceModel();if(!t||!e)return;const s=this._geometrySnapshot(),i=this.host._spaceH,o=!!this.host._pendingSplit,a=o?Ut(e.rooms,t.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh):null,n=o&&Array.isArray(t.walls)?t.walls:null;let r;if(this.host._pendingSplit){const e=t.rooms.find(t=>t.id===this.host._pendingSplit.roomId);if(!e)return this.host._pendingSplit=null,this.host._splitSel=null,void(this.host._roomDialog=!1);e.poly=this.host._pendingSplit.mainPoly.map(t=>[t[0]/lh,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this.host._pendingSplit.newPoly}else{if(!this.host._contourClosed)return;r=this.host._path.slice(0,-1)}const l=this.host._areaSel?this.host.hass.areas[this.host._areaSel]?.name:"",h={id:"r"+Date.now().toString(36),name:this.host._nameSel||l||this.host._t("room.default_name"),area:this.host._areaSel||null,poly:r.map(t=>[t[0]/lh,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!o&&this.host._activeDraftId){const e=Array.isArray(t.room_drafts)?t.room_drafts.find(t=>t.id===this.host._activeDraftId):null,s=xn(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,X),i=this._draftSegmentsForPath(this.host._path,e,s);i.length===r.length&&i.every(t=>!!t.id)&&(h.wall_ids=i.map(t=>t.id))}if(t.rooms.push(h),!o&&this.host._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),o){const e=this.host._normalizeWalls(a,this.host._openCuts());e.length?t.walls=e:n?.length?t.walls=n:delete t.walls}if(!o){const e=xn(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,X),s=e[0];if(null!=s){this.host._cfgEpoch++;const i=this.host._openCuts(),o=this.host._spaceModel();if(!o)return;let a=vs(t.walls,o.rooms,h.id,s,this.host._wallKeyPitch,i,lh);for(const t of Z(o.rooms,a,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh)){if(t.roomId!==h.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],i=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return G(s,[t[0],t[1],i[0],i[1]])<=.02*this.host._gridPitch});i>=0&&(a=et(a,t.a,t.b,e[i],this.host._wallKeyPitch,lh))}a=this.host._normalizeWalls(a,i),a.length?t.walls=a:delete t.walls}}const c=this._commitPhysicalGeometry(this.host._t(o?"history.split_room":"history.add_room"),s);this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._pendingSplit=null,this.host._splitSel=null;const d=this.host._areaSel;if(this.host._areaSel="",this.host._nameSel="",this.host._roomDialog=!1,this.host._regSignature="",this.host._maybeRebuildDevices(),!c)return;let p=0;if(d){const t=lh,e={...this.host._layout};for(const s of this.host._devices){if(s.area!==d||s.space!==this.host._space)continue;if(p++,this.host._layout[s.id])continue;const i=this.host._defPos[s.id];i&&(e[s.id]={s:this.host._space,x:i.x/lh,y:i.y/t},this.host._dirtyPos.add(s.id))}this.host._layout=e,this.host._persistLayout()}const u=this.host._model.find(t=>t.id===this.host._space)?.rooms.length||0;this.host._showToast(d?this.host._t("toast.room_saved",{n:u,added:p}):this.host._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this.host._wallRepairDiagnostic=null,this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),this.host._roomDialog=!1,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._splitSel=null,this.host._mergeSel=null,this.host._mergeDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this.host._resumeDraftBySpace[this.host._space];if(!t)return;const e=this.host._spaceModel()?.room_drafts.find(e=>e.id===t);e?(this.host._activeDraftId=t,this.host._path=e.points.map(t=>[...t]),this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.segments.map(t=>t.cm),e.id),this._clearPlanSnapHover()):delete this.host._resumeDraftBySpace[this.host._space]}_roomDialogCancel(){if(this.host._roomDialog=!1,this.host._roomEditId)return this.host._roomEditId=null,this.host._nameSel="",void(this.host._areaSel="");if(this.host._wallFaceBatch){const t=this.host._wallFaceBatch;return this.host._wallFaceBatch=null,this.host._path=t.activePath.map(t=>[...t]),this.host._draftSegmentCms=[...t.activeCms],this.host._activeDraftId=t.activeDraftId,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate()}if(this.host._pendingSplit)return this.host._pendingSplit=null,void(this.host._splitSel=null);this.host._undoPoint()}_openDeviceInbox(){this.host._deviceInboxReturn=null,this.host._deviceInbox=this.host._deviceInbox||{tab:"on_plan",search:"",showEntities:!1,onlyNew:!1,limit:100}}_closeMarkerDialog(){if(this.host._markerDialog=null,this.host._deviceInboxReturn){const t={...this.host._deviceInboxReturn};this.host._deviceInbox=t,this.host._deviceInboxReturn=null,t.anchor&&this.host.updateComplete.then(()=>requestAnimationFrame(()=>{const e=`.device-inbox-row[data-binding="${CSS.escape(t.anchor)}"]`;this.host.renderRoot.querySelector(e)?.scrollIntoView({block:"nearest"})}))}}_deviceInboxCandidates(t){return en({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:t,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}})}_deviceInboxRows(){const t=this.host._deviceInbox||this.host._deviceInboxReturn,e=!!t?.showEntities,s=[this.host._haRegistry.revision,this.host._cfgRev,this.host._cfgEpoch,this.host._regSignature,this.host._newSyncKey,e?1:0,this.host._showAll?1:0,l(this.host.hass,this.host._config?.language)].join("|");if(this.host._deviceInboxMemo?.key===s)return this.host._deviceInboxMemo.rows;const i=this._deviceInboxCandidates(e),o=new Set(i.map(t=>t.value));for(const t of this.host._markers)t.binding&&"virtual"!==t.binding&&o.add(t.binding);for(const t of this.host._devices)t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&o.add(`${t.bindingKind}:${t.bindingRef}`);const a=new Map;for(const t of o)a.set(t,this.host._bindingStatus(t));const n=this.host._areaToSpace,r={};for(const[t,e]of Object.entries(this.host.hass?.areas||{}))r[t]=n[t]?.room?.name||e?.name||t;for(const[t,e]of Object.entries(n))r[t]=e.room.name||r[t]||t;const h=Object.fromEntries(this.host._model.map(t=>[t.id,t.title])),c=Object.fromEntries(Object.entries(n).map(([t,e])=>[t,e.space])),d={},p=new Map;for(const[t,e]of Object.entries(this.host._fullRegistryHass.entities||{})){const s=String(e?.platform||"").trim();if(s&&(d[`entity:${t}`]=s),s&&e?.device_id){const t=p.get(e.device_id)||new Set;t.add(s),p.set(e.device_id,t)}}for(const[t,e]of p)d[`device:${t}`]=[...e].sort().join(", ");const u={};for(const[t,e]of Object.entries(this.host._fullRegistryHass.devices||{})){const s=`device:${t}`,i=p.get(t)||new Set,o=Array.isArray(e?.identifiers?.[0])?String(e.identifiers[0][0]||""):"";o&&!d[s]&&(d[s]=o);const a=[o,...i].some(t=>this.host._excluded.has(t));"service"===e?.entry_type?u[s]="service_entry":a?u[s]="excluded_integration":"Group"===e?.model?u[s]="grouped_light":/scene/i.test(e?.model||"")?u[s]="excluded_domain":(/bridge/i.test(`${e?.model||""}${e?.name||""}`)||"myheat"===o&&e?.via_device_id)&&(u[s]="represented_by_parent")}const _=function(t){const{devices:e,markers:s,candidates:i,statuses:o,newDeviceIds:a,showHiddenOnPlan:n,areaNames:r={},spaceNames:l={},spaceByArea:h={},integrationByBinding:c={},reasonByBinding:d={}}=t,p=new Map;for(const t of e)!t.virtual&&t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&p.set(`${t.bindingKind}:${t.bindingRef}`,t);const u=new Map,_=new Map;for(const t of s)t?.binding&&"virtual"!==t.binding&&(t.removed?_.set(t.binding,t):u.set(t.binding,t));const m=new Map(i.map(t=>[t.value,t])),g=new Set([...p.keys(),...u.keys(),...m.keys(),...[..._.keys()].filter(t=>m.has(t))]),f=[];for(const t of g){const e=p.get(t),s=u.get(t),i=s?void 0:_.get(t),g=m.get(t);let b;if(i&&g)b="readd";else if(!0===s?.hidden)b="hidden";else if(e||s)b="on_plan";else{if(!g)continue;b="available"}const v=e?.bindingStatus||o.get(t)||sn,y=s||i,$=on(t),w=e?.area||g?.areaId||y?.area||"",k=e?.space||y?.space||h[w]||"",x=e?.name||y?.name||g?.label||t,S=e?.model||y?.model||g?.model||"",M=!!g?.parentDeviceId&&p.has(`device:${g.parentDeviceId}`),D="readd"===b?"removed":"hidden"===b?s&&an(s)?d[t]||"automatic_hidden":"manual_hidden":"on_plan"===b?s?"visible_explicit":"visible_auto":d[t]||(M?"represented_by_parent":w&&k?"available":"no_bound_room"),C=!!e&&(!e.hidden||n),P="active"===v.kind,I=C&&(P||"ha_disabled"===v.kind&&n),T=[x,S,c[t],r[w],l[k],t,g?.sub].filter(Boolean).join(" ").toLocaleLowerCase();f.push({key:t,binding:t,category:b,status:v,reason:D,deviceId:e?.id,markerId:y?.id,name:x,icon:e?.icon||y?.icon||("entity"===$?"mdi:code-braces":"mdi:devices"),model:S,integration:c[t]||"",areaId:w,areaName:r[w]||"",spaceId:k,spaceName:l[k]||"",kind:$,isNew:!!e&&a.has(e.id),searchText:T,canFind:I,canEdit:!!e||!!s,canHide:"on_plan"===b&&P,canShow:"hidden"===b&&P,canAdd:("available"===b||"readd"===b)&&P})}const b=t=>"active"===t.status.kind?1:0;return f.sort((t,e)=>t.category.localeCompare(e.category)||Number(e.isNew)-Number(t.isNew)||b(t)-b(e)||t.name.localeCompare(e.name)||t.binding.localeCompare(e.binding))}({devices:this.host._devices,markers:this.host._markers,candidates:i,statuses:a,newDeviceIds:this.host._newIds,showHiddenOnPlan:this.host._showAll,areaNames:r,spaceNames:h,spaceByArea:c,integrationByBinding:d,reasonByBinding:u});return this.host._deviceInboxMemo={key:s,rows:_},_}_deviceForInboxRow(t){const e=t.deviceId?this.host._devices.find(e=>e.id===t.deviceId):null;if(e)return e;const s=t.markerId?this.host._markers.find(e=>e.id===t.markerId):null;return!s||s.removed?null:{id:s.id,name:t.name,model:t.model,area:t.areaId,space:t.spaceId||this.host._space,hidden:!0===s.hidden||"ha_disabled"===t.status.kind,userHidden:!0===s.hidden,bindingStatus:t.status,icon:t.icon,entities:"active"===t.status.kind?t.status.enabledEntityIds:[],allEntities:t.status.allEntityIds,primary:"active"===t.status.kind?t.status.enabledEntityIds[0]:void 0,marker:s,bindingKind:t.kind,bindingRef:t.binding.slice(t.binding.indexOf(":")+1),pdfs:s.pdfs||[]}}_openInboxMarker(t,e=!1){const s=this.host._deviceInbox;if(s){if(this.host._deviceInboxReturn={...s,anchor:t.key},this.host._deviceInbox=null,!e){const e=this._deviceForInboxRow(t);return void(e?this._openMarkerDialog(e):this._closeMarkerDialog())}this._openMarkerDialog(),this.host._markerDialog?this.host._markerDialog={...this.host._markerDialog,bindingMode:"ha",binding:t.binding,bindingOpen:!1,showEntities:"entity"===t.kind,name:""}:this._closeMarkerDialog()}}async _setInboxHidden(t,e){const s=this.host._deviceInbox,i=this.host._serverCfg;if(!s||!i||s.busy||"active"!==t.status.kind)return;const o=i.markers||[],a=o.find(e=>!e.removed&&e.binding===t.binding);if(!e&&!a)return;const n=a?.id||ys(t.binding,t.markerId,()=>`m_${Date.now().toString(36)}`),r=a?{...a,hidden:e}:{id:n,binding:t.binding,hidden:!0};i.markers=[...o.filter(e=>e.id!==n&&(e.binding!==t.binding||!0===e.removed)),r],this.host._deviceInbox={...s,busy:t.key,anchor:t.key};try{await this._saveConfigNow(),this.host._regSignature="",this.host._deviceInboxMemo=null,this.host._maybeRebuildDevices(),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("device_inbox.saved"))}catch(t){this.host._serverCfg===i&&(i.markers=o),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_findInboxDevice(t){if(!t.canFind)return;const e=this._deviceForInboxRow(t);if(!e)return;if(this.host._deviceInbox=null,e.space&&e.space!==this.host._space){if(!this.host._commitSpace(e.space))return;this.host._restoreZoom()}const s=()=>{const t=this.host._devices.find(t=>t.id===e.id)||e,s=this.host._pos(t);this.host._applyView(this.host._zoom,s.x,s.y),this.host._selId=t.id,this.host.requestUpdate(),window.setTimeout(()=>{this.host._selId===t.id&&(this.host._selId=null,this.host.requestUpdate())},1500)};requestAnimationFrame(()=>requestAnimationFrame(s))}_openMarkerDialog(t){if(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null,t&&this.host._ackNewDevice(t.id),this.host._norm)if(t){const e=t.marker,s=Object.prototype.hasOwnProperty.call(e||{},"is_light"),i=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),o=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),a=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),n=Object.prototype.hasOwnProperty.call(e||{},"value_source"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),l=Object.prototype.hasOwnProperty.call(e||{},"toggle_entity"),h=$s(e?.glow_color),c=this.host._devicePresentation(t,!0).valueBadge,d=ws(this.host._planHass,t,this.host._devices),p=ks(this.host._planHass,t,d);this.host._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this.host._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ms(t.marker?.display),rippleColor:b(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):1.5,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Ss(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:e?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:l,originalToggleEntity:e?.toggle_entity,tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:xs(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:s,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:i,originalLightEntity:e?.light_entity,glowMode:null!=h?.bri?"fixed":h?"color":"auto",glowColor:h?.c||this.host._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(h?.bri??1))),glowColorDrafted:!!h,glowBrightnessDrafted:null!=h?.bri,glowTouched:!1,originalHasGlowColor:o&&(!!h||null===e?.glow_color),originalGlowColor:h||(null===e?.glow_color?null:void 0),valueBadgeEnabled:a?!0===e?.value_badge?.enabled:!!c,valueBadgeSource:e?.value_badge?.source||c?.source||p,valueBadgePosition:e?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:a,originalValueBadge:e?.value_badge,valueSource:e?.value_source||null,valueSourceTouched:!1,originalHasValueSource:n,originalValueSource:e?.value_source,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this.host._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",roomTouched:!1,hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this.host._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:1.5,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",toggleEntity:"",toggleEntityTouched:!1,originalHasToggleEntity:!1,originalToggleEntity:void 0,tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this.host._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,valueSource:null,valueSourceTouched:!1,originalHasValueSource:!1,originalValueSource:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",roomTouched:!1,hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this.host._showToast(this.host._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ds)for(const[s,i]of Object.entries(this.host.hass.states))s.startsWith(e+".")&&this.host._planEntityAvailable(s)&&t.push({value:s,label:i?.attributes?.friendly_name||s,sub:this.host._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=en({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:!!this.host._markerDialog?.showEntities,currentBinding:this.host._markerDialog?.binding,currentDeviceId:this.host._markerDialog?.devId,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}}),e=(this.host._markerDialog?.bindingFilter||"").toLowerCase().trim(),s=e?t.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(e)):t;return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,s=this.host._t(t);if(s!==t)return s}return this.host._errText(t)}async _pickMarkerFiles(t){const e=t.target,s=e.files?[...e.files]:[];if(e.value="",!s.length||!this.host._markerDialog)return;const i=this.host._markerDialog.uploadId||this.host._markerDialog.devId||"new",o=[];for(const t of s)try{const e=new FormData;e.append("marker_id",i),e.append("file",t,t.name);const s=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await s.json().catch(()=>({}));if(!s.ok||a.error){const t={too_large:this.host._t("err.too_large",{mb:a.max_mb||50}),bad_ext:this.host._t("err.bad_ext"),unauthorized:this.host._t("err.unauthorized")};throw new Error(t[a.error]||a.error||"HTTP "+s.status)}o.push({name:a.name||t.name,url:a.url})}catch(e){this.host._showToast(this.host._t("toast.file_failed",{name:t.name,err:this.host._errText(e)}))}o.length&&this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:[...this.host._markerDialog.pdfs,...o]},this.host._showToast(this.host._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:this.host._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const s={c:b(t.glowColor,this.host._fillColors.glow_light.c)};"fixed"===t.glowMode&&(s.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=s}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}_markerToggleEntityFields(t){return(e={touched:t.toggleEntityTouched,originalHas:t.originalHasToggleEntity,original:t.originalToggleEntity,value:t.toggleEntity}).touched?e.value?{toggle_entity:e.value}:{}:e.originalHas?{toggle_entity:e.original??null}:{};var e}async _saveMarker(){const t=this.host._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return;if("virtual"===t.binding&&!t.name.trim())return void this.host._showToast(this.host._t("toast.virtual_name_required"));if("run"===e&&!t.tapTarget)return void this.host._showToast(this.host._t("toast.run_target_required"));if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)return void this.host._showToast(this.host._t("toast.value_badge_source_required"));if("ha"===t.bindingMode){const e=this.host._bindingStatus(t.binding),s=t.devId?this.host._markers.find(e=>e.id===t.devId):null,i=!s||s.binding!==t.binding;if("active"!==e.kind&&i)return void this.host._showToast(this.host._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===s?.hidden&&!t.hideFromPlan)return this.host._markerDialog={...t,hideFromPlan:!0},void this.host._showToast(this.host._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const s=this.host._serverCfg;if(!s)return;const i=this.host._cfgRev,o=_(s);let a=JSON.parse(JSON.stringify(s));const n=a.markers||[],r=ys(t.binding,t.devId,()=>"v_"+Date.now().toString(36)),l=t.devId,h=l?this.host._devices.find(t=>t.id===l):null,c=l?n.find(t=>t.id===l):null,d=!!c&&("string"==typeof c.area&&c.area.length>0||null===c.area&&!!c.space&&!!c.room_id),p="virtual"===t.binding||t.roomTouched||d,g="virtual"===t.binding||t.roomTouched?Cs(t.room):null;let f=d&&!t.roomTouched?c?.space||null:g?.space||null;const b=d&&!t.roomTouched?c?.area||null:g?.area||null,v=d&&!t.roomTouched?c?.room_id||null:g?.roomId||null,y=f||h?.space||null,$=y?this.host._spaceModelById(y):this.host._spaceModel();if(!$)return;const w=$.id;"virtual"!==t.binding||f||(f=w),this.host._markerDialog={...t,busy:!0};let k=null,x=!1;try{const c="virtual"===t.binding?[]:n.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),d=c.length>0,m=xs(t.binding,t.controls,this.host._bindingEntities(t.binding)),g=n.find(t=>t.id===r||t.id===l)?.vacuum||null,y={id:r,vacuum:g,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:m.length?m:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=nh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};p&&(y.space=f,y.area=b,y.room_id=v);const S=h?.marker?.room_id??null,M=t.roomTouched&&null!=h&&(h.space!==f||h.area!==b||S!==v);let D=!1;const C=t.uploadId||l;if(C&&C!==r&&y.pdfs?.length)try{const t=await this.host.hass.callWS({type:"houseplan/files/migrate",from_id:C,to_id:r}),e=t?.mapping||{};y.pdfs=Ps(y.pdfs,C,r,e),D=Object.keys(e).length>0}catch(t){this.host._showToast(this.host._t("toast.files_migrate_failed",{err:this.host._errText(t)}))}a.markers=n,l&&l!==r&&(a.markers=Is(a.markers,l,r)),l&&l!==r&&"derived_marker_state"===y.value_badge?.source?.kind&&y.value_badge.source.ref===`marker:${l}`&&(y.value_badge.source={kind:"derived_marker_state",ref:`marker:${r}`}),l&&l!==r&&"derived_marker_state"===y.value_source?.kind&&y.value_source.ref===`marker:${l}`&&(y.value_source={kind:"derived_marker_state",ref:`marker:${r}`}),a.markers=a.markers.filter(t=>t.id!==r&&t.id!==l&&("virtual"===y.binding||t.binding!==y.binding)),a.markers.push(y);const P=new Set(c);l&&l!==r&&P.add(l),P.delete(r),P.size&&a.settings?.marker_area_snapshot&&(a.settings={...a.settings,marker_area_snapshot:Ts(a.settings.marker_area_snapshot,P)});let I=null;const T=l?this.host._layout[l]:null,R=T?{s:T.s||h?.space||this.host._space,x:T.x,y:T.y}:l&&h&&this.host._defPos[l]?this.host._normPos(h.space,this.host._defPos[l].x,this.host._defPos[l].y):null;if(!d&&R&&R.s===w)r===l&&this.host._layout[r]&&!M||(I={s:R.s,x:R.x,y:R.y});else if(d||!this.host._layout[r]||M){let t=$.vb[0]+$.vb[2]/2,e=$.vb[1]+$.vb[3]/2;const s=v?$.rooms.find(t=>t.id===v):b?$.rooms.find(t=>t.area===b):void 0;s&&([t,e]=this.host._roomCenter(s)),I=this.host._normPos(w,t,e)}if(this.host._serverCfg!==s||this.host._cfgRev!==i||_(s)!==o)return this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),void this.host._showToast(this.host._t("toast.conflict"));if(a=this._prepareConfigCandidate(a),k=u(s,a,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=a,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.cancel(),await this._saveConfigNow(k),x=!0,I){const t=Rs(I);this.host._layout={...this.host._layout,[r]:t},this.host._noteLayoutRev(await this.host.hass.callWS({type:"houseplan/layout/update",device_id:r,pos:t}))}const z=new Set(c);l&&l!==r&&z.add(l),z.delete(r);for(const t of z)delete this.host._layout[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{});D&&C&&await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:C}).catch(()=>{}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_saved"))}catch(t){!x&&k&&(m(this.host,k,_),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteMarker(){const t=this.host._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this.host._devices.find(e=>e.id===t.devId):null,s=this.host._markers.find(e=>e.id===t.devId);if(!e&&!s)return;const i=e?"virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"":s.binding;if(!i)return;const o=t.name||this.host._t("device.fallback"),a=t.devId,n=await this.host._confirmDanger({key:"remove-marker",kind:"destructive",title:this.host._t("confirm.remove_marker_title"),message:this.host._t("confirm.remove_marker_body"),objectName:o,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._markerDialog;if(!n||!r||r.busy||r.devId!==a)return;const l=this.host._devices.find(t=>t.id===a),h=this.host._markers.find(t=>t.id===a);if(!l&&!h)return;const c=this.host._serverCfg;if(!c)return;c.markers=c.markers||[];const d=c.markers,p=l?"virtual"===l.bindingKind?"virtual":l.bindingKind&&l.bindingRef?`${l.bindingKind}:${l.bindingRef}`:"":h.binding;if(!p||p!==i)return;const u=zs(c.markers,a,p,"virtual"===p);c.markers=As(u.markers,u.cleanupIds);const _=u.cleanupIds;_.size&&c.settings?.marker_area_snapshot&&(c.settings={...c.settings,marker_area_snapshot:Ts(c.settings.marker_area_snapshot,_)}),this.host._markerDialog={...r,busy:!0};try{await this._saveConfigNow();for(const t of _){delete this.host._layout[t],delete this.host._defPos[t],this.host._dirtyPos.delete(t),this.host._sentPos.delete(t);const e=this.host._activityRt.get(t);e&&clearTimeout(e.timer),this.host._activityRt.delete(t),this.host._vacRt.delete(t),delete this.host._vacSrvTrails[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this.host._deviceInboxReturn&&(this.host._deviceInboxReturn={...this.host._deviceInboxReturn,tab:"readd",anchor:p}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._infoCard?.id===a&&this.host._closeInfoCard(),this.host._selId===a&&(this.host._selId=null),this.host._drag?.id===a&&(this.host._drag=null),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_removed"))}catch(t){this.host._serverCfg===c&&(c.markers=d),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_openSpaceDialog(t,e){if(this.host._serverStorage&&this.host._serverCfg)if("edit"===t){const s=this.host._serverCfg.spaces.find(t=>t.id===e);if(!s)return;const i=Fs(s),o=s.settings?.custom_fill&&"object"==typeof s.settings.custom_fill?Ns(s.settings.custom_fill):null,a="none"===i.fill?{...o||Hs,a:0}:o;this.host._spaceDialog={mode:t,spaceId:e,title:s.title,planUrl:s.plan_url||null,planFile:null,source:s.plan_url?"file":"draw",showBorders:i.showBorders,showNames:i.showNames,zeroWallStyle:Es(s),displayTouched:!0,hideDecor:i.hideDecor,hideOpenings:i.hideOpenings,roomColor:i.color,roomOpacity:i.opacity,fillMode:"none"===i.fill?"custom":i.fill,customFill:a,glowEnabled:i.glow,bgColor:i.bgColor,bgMode:"static"===s.settings?.bg_mode||"daynight"===s.settings?.bg_mode?s.settings.bg_mode:null,northDeg:Zt({},s.settings),sunRays:"boolean"==typeof s.settings?.sun_rays?s.settings.sun_rays:null,tempMin:i.tempMin,tempMax:i.tempMax,showLqi:i.showLqi??this.host._config?.show_signal??!0,cardFontScale:i.cardFontScale,labelTemp:i.labelTemp,labelHum:i.labelHum,labelLqi:i.labelLqi,labelLight:i.labelLight,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,cellCmInput:Ls(Number(s.cell_cm)>0?Number(s.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1}}else this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...io(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:js,roomOpacity:Ws,fillMode:"custom",customFill:{...Hs,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:Bs,tempMax:Os,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Gs(this.host._imperial),cellCmInput:Ls(Gs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1};else this.host._showToast(this.host._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,s=e.files?.[0];if(!s||!this.host._spaceDialog)return;e.value="",this._decorAssetGuardReplace=null;const i=await so(s);if("reject"===i.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===i.kind)return void(this.host._backdropGuard=i.state);const o=await oo(s,i.ext,s.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:o})}_renderBackdropGuard(){const t=this._decorAssetGuardReplace;return null!==t?ao(this.host,()=>{},()=>{this.host._backdropGuard=null,this._decorAssetGuardReplace=null},this.host.hass,(e,s)=>this._uploadDecorImage(e,s,t),(this.host._backdropGuard?.file.size||0)<=2097152)??d:ao(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??d}_useServerPlan(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this.host._display(t);if(e){const s=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:0),s.onerror=()=>t(0),s.src=e}),i=this.host._spaceDialog;return i&&i.planUrl===t&&Number.isFinite(s)&&s>0?(this.host._spaceDialog={...i,savedAspect:s},s):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const e=this.host._spaceDialog,s=e?.saved?.find(e=>e.name===t);if(!e||!s||s.used_by.length||s.url===e.planUrl)return;const i=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),o=this.host._spaceDialog,a=o?.saved?.find(e=>e.name===t);if(i&&o&&a&&a.url===s.url&&a.modified===s.modified&&!a.used_by.length&&a.url!==o.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this.host._spaceDialog;e?.saved&&(this.host._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
    ${this.host._t("space.loading")}
    `;const e=t.saved||[];if(!e.length)return p`
    ${this.host._t("space.no_saved")}
    `;return p`
    - ${e.map(e=>{return p` -
    - -
    - ${e.name} - ${s=e.size,s>=1048576?(s/1048576).toFixed(1)+" MB":Math.round(s/1024)+" KB"}${e.used_by.length?" · "+this.host._t("space.used_by",{list:e.used_by.join(", ")}):""} -
    - - -
    `;var s})} -
    `}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const e="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const s="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let i=null;if("file"===t.source&&t.planFile){i={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:s,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!i&&"file"===t.source&&t.planUrl&&!o&&this.host._aspectJob&&(o=await this.host._aspectJob||null);const a=this.host._serverCfg;let n;if("create"===t.mode)n=no(s,t.title.trim()),a.spaces.push(n);else{if(n=a.spaces.find(t=>t.id===s),!n)throw new Error("space "+s+" is gone from the config");n.title=t.title.trim()}i?(n.plan_url=i.url,n.plan_aspect=i.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==n.plan_url&&(n.plan_url=t.planUrl,n.plan_aspect=o),"draw"===t.source&&(n.plan_url=null,n.plan_aspect=null,delete n.plan_x,delete n.plan_y,delete n.plan_scale,delete n.plan_scale_x,delete n.plan_scale_y,delete n.plan_angle),n.settings={...n.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):Bs,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):Os,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},n.zero_wall_style=t.zeroWallStyle,n.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(ah,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(n.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length?this._openNextImport():e||this.host._importTotal>0?(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._showToast(this.host._t(e&&!this.host._importTotal?"toast.space_added_onboard":"import.done"))):(this.host._showToast("create"===t.mode?this.host._t("toast.space_added"):this.host._t("toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?this._setMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._saveNav())))}catch(t){"conflict"!==t?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this.host._serverCfg;if(!e)return;const s=e.spaces.find(e=>e.id===t.spaceId),i=ro(e,this.host._layout||{},t.spaceId||""),o=1===e.spaces.length&&e.spaces[0]?.id===t.spaceId;if(i.count&&!o)return void(this.host._spaceDialog={...t,deleteBlockers:i.count});if(!s)return;const a=t.spaceId,n=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:lo(this.host._t("confirm.delete_space_body"),this.host._t("confirm.delete_space_vac_routes"),i.routeCount),objectName:s.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._spaceDialog,l=this.host._serverCfg;if(!n||!r||"edit"!==r.mode||r.busy||r.spaceId!==a||!l)return;if(!l.spaces.find(t=>t.id===a))return;const h=ro(l,this.host._layout||{},a),c=1===l.spaces.length&&l.spaces[0]?.id===a;if(!h.count||c){this.host._spaceDialog={...r,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:a,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(e,s),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===a&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=ro(e,this.host._layout||{},a),s=1===e.spaces.length&&e.spaces[0]?.id===a;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...r,deleteBlockers:h.count}}async _saveConfigNow(t=null){this.host._cfgEpoch++;try{await this._writeConfig(t)}catch(t){throw t?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===t?.code&&await this.host._reloadConfigOnly(),t}}_startImport(){const t=this.host._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,e.length?(this.host._importQueue=e,this.host._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();void 0!==t&&(this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...io(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:js,roomOpacity:Ws,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:Bs,tempMax:Os,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Gs(this.host._imperial),cellCmInput:Ls(Gs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._showToast(this.host._t("import.done")))}_renderImportDialog(){const t=this.host._importDialog,e=t.floors.filter(t=>t.checked).length;return p`this.host._importDialog=null}> -
    -
    ${this.host._t("import.hint")}
    - ${t.floors.map((e,s)=>p``)} -
    -
    - - - -
    -
    `}_supportPatch(t,e){const s=this.host._supportDialog;if(!s||s.draftId!==t)return null;const i={...s,...e};return this.host._supportDialog=i,i}async _focusSupport(t){await this.host.updateComplete,this.host.renderRoot.querySelector(t)?.focus({preventScroll:!0})}_updateSupportDraft(t,e){const s=this.host._supportDialog;if(!s||"building"===s.status||"sending"===s.status||"success"===s.status)return;const i={...s,[t]:e},o=Al({...i,attach:!1,preview:null});"message_required"===o&&"validation.message_required"===s.errorCode||"message_too_long"===o||"contact_too_long"===o?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:`validation.${o}`}):i.attach&&i.preview&&i.preview.expiresAt<=Date.now()?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:"support_preview_expired"}):this._supportPatch(s.draftId,{[t]:e,status:i.attach&&i.preview?"ready":"idle",errorCode:""})}async _discardSupportPreview(t){if(t)try{await this.host.hass.callWS({type:"houseplan/support/preview/discard",token:t})}catch{}}async _closeSupportDialog(){const t=this.host._supportDialog;if(t){if("building"===t.status||"sending"===t.status){const t=l(this.host.hass,this.host._config?.language);if(!await this.host._confirmDanger({key:"close-support-busy",kind:"warning",title:vo(t,"support.close_busy_title"),message:vo(t,"support.close_busy_body"),confirmLabel:this.host._t("btn.close"),cancelLabel:this.host._t("btn.cancel")}))return await this.host.updateComplete,void this.host.renderRoot.querySelector("#support-dialog")?.rejectClose()}clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this.host._supportDialog=null,t.preview?.token&&"success"!==t.status&&this._discardSupportPreview(t.preview.token)}}_scheduleSupportExpiry(t,e){clearTimeout(this._supportExpiryTimer);const s=Math.max(0,e.expiresAt-Date.now());this._supportExpiryTimer=window.setTimeout(()=>{const s=this.host._supportDialog;s&&s.draftId===t&&s.preview?.token===e.token&&s.attach&&"success"!==s.status&&"sending"!==s.status&&this._supportPatch(t,{status:"error",errorCode:"support_preview_expired"})},Math.min(s+20,2147483647))}_supportFacts(){const t=this.host._haRegistry;return Nl({userAgent:globalThis.navigator?.userAgent||"",language:l(this.host.hass,this.host._config?.language),registryAccess:t.access,registryLastSuccess:t.lastSuccess})}_supportPreviewRequestIsCurrent(t,e){const s=this.host._supportDialog;return e===this._supportPreviewGeneration&&s?.draftId===t&&s.attach}async _buildSupportPreview(t){const e=this.host._supportDialog;if(!e||e.draftId!==t||!e.attach||!Il(this.host._haSupportApi))return;const s=++this._supportPreviewGeneration;this._supportPatch(t,{status:"building",errorCode:""});let i="";try{const e=await this.host.hass.callWS({type:"houseplan/support/preview",card_version:eh,...this._supportFacts(),draft_id:t}),o=e&&"object"==typeof e?e:{},a=Date.now(),n="string"==typeof o.text?o.text:"",r=Number(o.size),l=Number(o.expires_in),h=Number(o.spaces),c=String(o.token||"");/^[0-9a-f]{48}$/.test(c)&&(i=c);const d=String(o.sha256||""),p=Number(o.version),u=String(o.format||""),_=(new TextEncoder).encode(n).byteLength;if(!/^[0-9a-f]{48}$/.test(c)||!/^[0-9a-f]{64}$/.test(d)||"houseplan-support-package"!==u||1!==p||!Number.isInteger(r)||r!==_||!Number.isInteger(h)||h<0||!Number.isFinite(l)||l<=0||l>600||!n.endsWith("\n"))throw{code:"support_rejected"};const m={token:c,expiresAt:a+1e3*l,size:r,sha256:d,spaces:h,format:u,version:p,text:n,preparedAt:a};if(!this._supportPreviewRequestIsCurrent(t,s))return i="",void this._discardSupportPreview(c);if(!this._supportPatch(t,{status:"ready",preview:m,errorCode:"",rawOpen:!1}))return i="",void this._discardSupportPreview(c);i="",this._scheduleSupportExpiry(t,m)}catch(e){if(i){const t=i;i="",this._discardSupportPreview(t)}if(!this._supportPreviewRequestIsCurrent(t,s))return;if(!this._supportPatch(t,{status:"error",errorCode:Hl(e)}))return;this._focusSupport("#support-error")}}async _setSupportAttachment(t){const e=this.host._supportDialog;if(!e||"sending"===e.status||"success"===e.status)return;const s=e.preview?.token||"";if(!t)return this._supportPreviewGeneration+=1,clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPatch(e.draftId,{attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:""}),void(s&&this._discardSupportPreview(s));this._supportPatch(e.draftId,{attach:!0,errorCode:""}),await this._buildSupportPreview(e.draftId)}async _refreshSupportPreview(){const t=this.host._supportDialog;t&&t.attach&&"building"!==t.status&&"sending"!==t.status&&await this._buildSupportPreview(t.draftId)}_downloadSupportPreview(){const t=this.host._supportDialog?.preview;if(!t)return;const e=new Blob([t.text],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(e),i=document.createElement("a");i.href=s,i.download=`houseplan-support-${t.sha256.slice(0,12)}.json`,i.style.display="none",document.body.append(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0)}async _copySupportText(t,e){const s=l(this.host.hass,this.host._config?.language);try{await navigator.clipboard.writeText(t),this.host._showToast(vo(s,e))}catch{this.host._showToast(vo(s,"support.copy_failed"))}}async _submitSupport(){const t=this.host._supportDialog;if(!t||!Il(this.host._haSupportApi))return;const e=Al(t);if(e)return this._supportPatch(t.draftId,{status:"error",errorCode:`validation.${e}`}),void this._focusSupport("#support-message");if(!Fl(t))return;const s=Rl(t),i=this._supportPatch(t.draftId,{status:"sending",errorCode:"",idempotencyKey:s.idempotencyKey,submissionFingerprint:s.fingerprint});if(i)try{const e=await this.host.hass.callWS({type:"houseplan/support/submit",message:i.message.trim(),contact:i.contact.trim(),...i.attach&&i.preview?{preview_token:i.preview.token}:{},idempotency_key:i.idempotencyKey}),s=e&&"object"==typeof e&&"report_id"in e?String(e.report_id||""):"";if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(s))throw{code:"support_unavailable"};if(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,!this._supportPatch(t.draftId,{status:"success",reportId:s,errorCode:""}))return;this._focusSupport("#support-receipt")}catch(e){if(!this._supportPatch(t.draftId,{status:"error",errorCode:Hl(e)}))return;this._focusSupport("#support-error")}}_supportErrorText(t){const e=l(this.host.hass,this.host._config?.language);if(t.errorCode.startsWith("validation.")){return vo(e,`support.validation.${t.errorCode.slice(11)}`)}return vo(e,`support.error.${t.errorCode||"support_unavailable"}`)}_supportMessageKeydown(t){"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this._submitSupport())}_renderSupportDialog(){const t=this.host._supportDialog,e=l(this.host.hass,this.host._config?.language),s="ru"===e?"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.ru.md":"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.md",i=Il(this.host._haSupportApi),o=(t,s)=>vo(e,t,s),a="building"===t.status||"sending"===t.status,n=Al(t),r=t.preview?Math.max(0,Math.floor((Date.now()-t.preview.preparedAt)/6e4)):0,h="error"===t.status&&!t.errorCode.startsWith("validation."),c="validation.contact_too_long"===t.errorCode,u="validation.message_required"===t.errorCode||"validation.message_too_long"===t.errorCode;return p`{this._closeSupportDialog()}}> -
    -
    -

    ${o("support.about_group")}

    -
    ${this.host._t("gs.about_version",{v:eh})}
    - -
    -
    -

    ${o("support.guide_group")}

    - - ${o("support.guide")} -
    - ${i?p` -
    -

    ${o("support.form_group")}

    - - this._updateSupportDraft("contact",t.target.value)} /> - - - -

    ${o("support.attach_hint")}

    - ${t.attach?p`

    - - ${o("support.geometry_warning")} -

    `:d} - ${"building"===t.status?p`
    - ${o("support.building")} -
    `:d} - ${t.attach&&t.preview?p` -
    -
    ${o("support.preview_summary",{version:t.preview.version,spaces:t.preview.spaces,size:(_=t.preview.size,(Math.max(0,_)/1024).toFixed(_<10240?1:0))})}
    -
    SHA-256${t.preview.sha256}
    -
    ${o("support.prepared",{n:r})}
    -
    this._supportPatch(t.draftId,{rawOpen:e.currentTarget.open})}> - ${o("support.show_data")} - ${t.rawOpen?p``:d} -
    -
    - - -
    -
    `:d} -

    ${o("support.privacy")}

    - ${"error"===t.status?p` - `:d} - ${"error"===t.status&&t.attach&&!t.preview?p` -
    - -
    `:d} - ${h?p` -
    - ${o("support.manual_recovery")} -
    - - ${t.preview?p``:d} - Telegram - GitHub -
    -
    `:d} - ${"success"===t.status?p` -
    - ${o("support.success",{id:t.reportId})} - -
    `:d} -
    `:p` -
    - - ${o("support.update_required")} -
    `} -
    -
    - - - ${i&&"success"!==t.status?p` - `:d} -
    -
    `;var _}_preflightDiagnostics(t,e){const s=new Map((e?.spaces||[]).map(t=>[String(t?.id||""),t]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:eh,checkedAt:(new Date).toISOString(),preflightFingerprint:t.fingerprint,failures:t.failures.map(t=>({spaceId:t.spaceId,displayName:t.displayName,reason:t.reason,detail:t.detail??null,spaceGeometryFingerprint:s.has(t.spaceId)?me(s.get(t.spaceId)):null}))}}_reportPreflightFailure(t,e){t.ok||t.fingerprint===this.host._reportedPreflightFingerprint||(this.host._reportedPreflightFingerprint=t.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(t,e)))}_preflightVersionsDiffer(){const t=this.host._haIntegrationVersion;return"string"==typeof t&&t.length>0&&t!==eh}async _copyPreflightDiagnostics(){const t=this.host._alignDialog?.preflight;if(!t||t.ok)return;const e=JSON.stringify(this._preflightDiagnostics(t,this.host._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(e),this.host._preflightClipboardFallback=null,this.host._showToast(this.host._t("gs.preflight_copied"))}catch{this.host._preflightClipboardFallback=e}}_checkOptimizeGeometryImpl(t){return qs(t,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)})})}_checkSpacePhysicalGeometryImpl(t,e,s){return Us(t,e,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)}),captureWallGeometry:s?(t,e)=>s(e):void 0})}_optimizeReferenceContext(t){const e=this.host._haRegistry,s=this.host._fullRegistryHass,i={},o=(...t)=>{for(const e of t){const t=String(e||"").trim();if(t)return t}return""};for(const[t,s]of Object.entries(e.devices||{}))i[t]=o(s?.name_by_user,s?.name,s?.model);for(const[t,s]of Object.entries(e.entities||{}))i[`lg_${t}`]=o(this.host.hass?.states?.[t]?.attributes?.friendly_name,s?.name,s?.original_name);for(const t of this.host._devices)i[t.id]=o(t.name,i[t.id]);for(const t of this.host._serverCfg?.markers||[]){const e=String(t.binding||"").indexOf(":"),a=e>0?t.binding.slice(0,e):"",n=e>0?t.binding.slice(e+1):"",r="device"===a?s?.devices?.[n]:"entity"===a?s?.entities?.[n]:null;i[t.id]=o(t.name,"device"===a?r?.name_by_user:null,r?.name,"entity"===a?this.host.hass?.states?.[n]?.attributes?.friendly_name:null,i[t.id])}return{effectiveAreaByMarker:Object.fromEntries(this.host._devices.filter(t=>!t.virtual&&!!t.area).map(t=>[t.id,t.area])),ownerRoster:{authoritative:e.authoritative,deviceIds:Object.keys(e.devices||{}),entityIds:[...new Set([...Object.keys(e.entities||{}),...Object.keys(this.host.hass?.states||{})])],names:i},removeLiveMissingPositions:t}}_previewAlignDialog(t){if(!this.host._norm||!this.host._serverCfg)return;const e=this.host._serverCfg.spaces||[];let s;try{s=Ml(this.host._serverCfg,this.host._layout||{},this._optimizeReferenceContext(t))}catch(t){return void this._showWallModelMigrationBlocked(t)}const i=s.changed?this.host._checkOptimizeGeometry(s.config):null;i&&this._reportPreflightFailure(i,s.config);const o=Math.ceil(10*s.report.maxShiftCm)/10,a=e.find(t=>null!=t?.id&&String(t.id)===s.report.maxSpace),n=e.length>1&&a?String(a.title||a.id):"";this.host._preflightClipboardFallback=null,this.host._alignDialog={report:s.report,config:s.config,layout:s.layout,cm:o,where:n,preflight:i,changed:s.changed,busy:!1,removeLiveMissingPositions:t}}async _runAlignToGrid(){let t=this.host._alignDialog;if(!t||t.busy||!this.host._serverCfg||!t.changed||!t.preflight?.ok)return;const e=_(t.config);if(t.preflight.fingerprint!==e){const e=this.host._checkOptimizeGeometry(t.config);if(this._reportPreflightFailure(e,t.config),t={...t,preflight:e},this.host._alignDialog=t,!e.ok)return}this._clearGeometryGesture(),this.host._alignDialog={...t,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain;const e=await this.host.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});this.host._serverCfg=t.config,this.host._layout=t.layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._cfgRev=e?.config_rev??this.host._cfgRev+1,this.host._layoutRev=e?.layout_rev??this.host._layoutRev+1,this.host._canOptimizeUndo=!!e?.can_undo,this.host._undoKind=e?.can_undo?"optimize":null,this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host._alignDialog=null,this.host._preflightClipboardFallback=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.coordsCanonicalized+t.report.latticeCoordinatesCanonicalized+t.report.wallsMerged+t.report.spansMerged+t.report.partitionsMerged+t.report.partitionsReconciled+t.report.openingsRehosted+t.report.wallsStraightened),r:String(t.report.spaceRefsRemapped+t.report.roomRefsRemapped+t.report.positionsRemapped+t.report.markersDetached+t.report.orphanRoomLabelsRemoved+t.report.orphanDevicePositionsRemoved+t.report.orphanGroupPositionsRemoved)}))}catch(t){if(this.host._alignDialog&&(this.host._alignDialog={...this.host._alignDialog,busy:!1}),"wall_model_client_outdated"===t?.code)return void this.host._showToast(this.host._t("toast.wall_model_client_outdated"));"conflict"===t?.code&&await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _undoPlanOptimization(){if(!this.host._canOptimizeUndo||this.host._optimizeUndoBusy)return;const t=this.host._undoKind;this._clearGeometryGesture(),this.host._optimizeUndoBusy=!0,this.host.requestUpdate();try{await this.host.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});const[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._serverCfg=e?.config||this.host._serverCfg,this.host._cfgRev=e?.rev??this.host._cfgRev,this.host._layout=s?.layout||this.host._layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._layoutRev=s?.rev??this.host._layoutRev,this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host.requestUpdate(),this.host._showToast(this.host._t("import"===t?"backup.import_undone":"gs.optimize_undone"))}catch(t){this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}finally{this.host._optimizeUndoBusy=!1,this.host.requestUpdate()}}async _runBackupExport(){const t=this.host._backupExportDialog;if(t&&!t.busy){this.host._backupExportDialog={...t,busy:!0,error:""};try{const e=await this.host.hass.callWS({type:"houseplan/export/create",kind:t.kind,space_id:"space"===t.kind?this.host._space:void 0,..."space"===t.kind&&t.planOnly?{plan_only:!0}:{},card_version:eh}),s=new Blob([JSON.stringify(e.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),i=URL.createObjectURL(s),o=document.createElement("a");o.href=i,o.download=e.filename||`houseplan-${t.kind}.json`,o.style.display="none",document.body.append(o),o.click(),o.remove(),window.setTimeout(()=>URL.revokeObjectURL(i),0),this.host._backupExportDialog=null,this.host._showToast(this.host._t("backup.export_done"))}catch(t){this.host._backupExportDialog&&(this.host._backupExportDialog={...this.host._backupExportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _pickBackupImport(t){const e=t.target,s=e.files?.[0];if(e.value="",s){this.host._settingsDialog=null,this.host._backupImportDialog={filename:s.name,size:s.size,token:"",preview:null,expectedConfigRev:this.host._cfgRev,expectedLayoutRev:this.host._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain,this.host._persistLayout.pending()&&this.host._persistLayout.flush();const t="/api/houseplan/import/preview?duplicate_policy=skip",e={method:"POST",body:s,headers:{"content-type":"application/json"}},i=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth(t,e):await fetch(t,{...e,headers:{...e.headers,...this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error)throw Object.assign(new Error(o.message||o.error||`HTTP ${i.status}`),{code:o.error});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,token:o.token,preview:o.preview,expectedConfigRev:o.expected_config_rev,expectedLayoutRev:o.expected_layout_rev,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _setBackupDuplicatePolicy(t){const e=this.host._backupImportDialog;if(e&&!e.busy&&e.token&&t!==e.duplicatePolicy){this.host._backupImportDialog={...e,duplicatePolicy:t,busy:!0,error:""};try{const s=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:t});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,preview:{...e.preview,...s.preview},expectedConfigRev:s.expected_config_rev,expectedLayoutRev:s.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,duplicatePolicy:e.duplicatePolicy,preview:e.preview,confirmMissing:e.confirmMissing,busy:!1,error:this._backupErrorText(t)})}}}async _applyBackupImport(){const t=this.host._backupImportDialog;if(!t||t.busy||!t.token||!t.preview)return;if(t.preview.confirmation_required&&!t.confirmMissing)return;this.host._backupImportDialog={...t,busy:!0,error:""};const e=this.host._space;try{const s=await this.host.hass.callWS({type:"houseplan/import/apply",token:t.token,expected_config_rev:t.expectedConfigRev,expected_layout_rev:t.expectedLayoutRev,duplicate_policy:t.duplicatePolicy,confirm_missing_content:t.confirmMissing}),[i,o]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(i,o),this.host._geometryHistory.clear(),this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._defPos={},this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._visibleDeviceSnapshot=null,this.host._candidateDeviceSnapshot=null,this.host._stagedDeviceSnapshotToken=-1,this.host._capturedSnapshotConfigEpoch=-1,this.host._regSignature="",this.host._signer.invalidate(this.host.hass),this.host._resign(),this.host._maybeRebuildDevices();const a=this.host._serverCfg?.spaces||[],n="space"===s.kind&&s.space_id?s.space_id:a.some(t=>t.id===e)?e:a[0]?.id||this.host._space;this.host._hasFixedFloor?this.host._adoptInitialSpace(this.host._model,!0):this.host._commitSpace(n),this.host._backupImportDialog=null,this.host._cacheSnapshot(),this.host.requestUpdate();const r="space"===s.kind?t.preview.counts:s.counts;this.host._showToast(this.host._t("space"===s.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(s.repaired_target_refs||0)}))}catch(t){if("conflict"===t?.code&&this.host._backupImportDialog?.token)try{const t=this.host._backupImportDialog,e=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:t.duplicatePolicy});return void(this.host._backupImportDialog={...t,preview:{...t.preview,...e.preview},expectedConfigRev:e.expected_config_rev,expectedLayoutRev:e.expected_layout_rev,confirmMissing:!1,busy:!1,error:this.host._t("backup.revalidated")})}catch(e){t=e}this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}_renderBackupExportDialog(){const t=this.host._backupExportDialog,e=(this.host._serverCfg?.spaces||[]).find(t=>t.id===this.host._space);return p`this.host._backupExportDialog=null}> -
    -
    ${this.host._t("backup.export_hint")}
    - - - ${"space"===t.kind&&e?p``:d} -
    ${this.host._t("backup.privacy_warning")}
    - ${t.error?p``:d} -
    -
    - - - -
    -
    `}_renderBackupImportDialog(){const t=this.host._backupImportDialog,e=t.preview,s=(e?.content||[]).filter(t=>"decor_asset"===t.kind),i=new Set(s.map(t=>t.url)).size,o=s.filter(t=>"missing_preserved"===t.state),a=new Set(o.map(t=>t.url)).size,n=e?.counts||{},r=e?.reference_report||{},l=t=>Object.values(t||{}).reduce((t,e)=>t+(Number(e)||0),0),h=[["incoming_remapped",l(r.remapped?.incoming)],["target_repaired",l(r.remapped?.target)],["preserved_unresolved",l(r.preservedUnresolved)],["collisions",l(r.collisions)],["dropped_links",l(r.droppedIncomingLinks)],["bounded_lineages",Number(r.boundedLineages)||0]].filter(([,t])=>t>0);return p`this.host._backupImportDialog=null}> -
    -
    ${t.filename}${(t.size/1024).toFixed(1)} KB
    - ${t.busy&&!e?p`
    ${this.host._t("backup.reading")}
    `:d} - ${t.error?p``:d} - ${e?p` -
    - ${this.host._t("full"===e.kind?"backup.full":"backup.current_space")} - ${e.plan_only?p`${this.host._t("backup.plan_only_preview")}`:d} - ${this.host._t("same"===e.source?"backup.same_source":"backup.foreign_source")} - ${this.host._t("backup.created",{value:e.created_at||"—"})} - ${this.host._t("backup.versions",{card:e.card_version||"—",integration:e.integration_version||"—",model:String(e.model_version??"—")})} -
    -
    - ${this.host._t("backup.count_spaces",{n:"full"===e.kind?`${e.current_counts?.spaces||0} → ${n.spaces||0}`:String(n.spaces||0)})} - ${this.host._t("backup.count_rooms",{n:"full"===e.kind?`${e.current_counts?.rooms||0} → ${n.rooms||0}`:String(n.rooms||0)})} - ${this.host._t("backup.count_walls",{n:"full"===e.kind?`${e.current_counts?.walls||0} → ${n.walls||0}`:String(n.walls||0)})} - ${this.host._t("backup.count_openings",{n:"full"===e.kind?`${e.current_counts?.openings||0} → ${n.openings||0}`:String(n.openings||0)})} - ${this.host._t("backup.count_decor",{n:"full"===e.kind?`${e.current_counts?.decor||0} → ${n.decor||0}`:String(n.decor||0)})} - ${this.host._t("backup.count_markers",{n:"full"===e.kind?`${e.current_counts?.markers||0} → ${n.markers||0}`:String(n.markers||0)})} - ${this.host._t("backup.count_layout",{n:"full"===e.kind?`${e.current_counts?.layout||0} → ${n.layout||0}`:String(n.layout||0)})} -
    - ${e.bindings?p`
    ${this.host._t("backup.bindings",{device:String(e.bindings.device||0),entity:String(e.bindings.entity||0),virtual:String(e.bindings.virtual||0),legacy:String(e.legacy_positions||0)})}
    ${this.host._t("backup.binding_status",{active:String(e.bindings.active||0),disabled:String(e.bindings.disabled||0),missing:String(e.bindings.missing||0)})}
    `:d} - ${e.missing_areas?.length?p`
    ${this.host._t("backup.missing_areas",{areas:e.missing_areas.join(", ")})}
    `:d} - ${e.dropped_marker_links?p`
    ${this.host._t("backup.dropped_marker_links",{n:String(e.dropped_marker_links)})}
    `:d} - ${e.repaired_target_refs?p`
    ${this.host._t("backup.repaired_target_refs",{n:String(e.repaired_target_refs)})}
    `:d} - ${e.preserved_unresolved_refs?p` -
    ${this.host._t("backup.preserved_unresolved_refs",{n:String(e.preserved_unresolved_refs)})}
    ${this.host._t("backup.preserved_unresolved_hint")}
    - `:d} - ${h.length?p`
    - ${this.host._t("backup.import_details")} -
    - ${h.map(([t,e])=>p`${this.host._t(`backup.import_detail.${t}`,{n:String(e)})}`)} - ${(r.examples||[]).slice(0,8).map(t=>p` - ${t.owner} → ${t.reference} - `)} -
    -
    `:d} - ${"full"===e.kind?p` -
    ${this.host._t("backup.replace_warning")}
    - ${"foreign"===e.source?p`
    ${this.host._t("backup.foreign_bookkeeping")}
    `:d}`:p` -
    ${this.host._t("backup.final_name")}${e.space_title}
    -
    ${this.host._t("backup.target_settings")}
    - ${e.duplicates?p`
    ${this.host._t("backup.duplicates",{n:String(e.duplicates)})} - - -
    `:d}`} - ${e.content?.length?p`
    - ${this.host._t("backup.content")} - ${s.length?p`${this.host._t("backup.decor_images_summary",{assets:i,objects:s.length,missing:a})}`:d} - ${e.content.filter(t=>"decor_asset"!==t.kind).map(t=>p`${t.url} · ${this.host._t("available"===t.state?"backup.content_available":"external"===t.state?"backup.content_external":"backup.content_detach_required")}`)} -
    `:d} - ${e.confirmation_required?p``:d} - `:d} -
    -
    - - - ${e?p``:d} -
    -
    `}_setFillColor(t,e){const s=this.host._settingsDialog;this.host._settingsDialog={...s,colors:{...s.colors,[t]:{...s.colors[t],...e}}}}_updateDecorStyle(t){this.host._decorStyle=t,null!==this._decorStylePersistTimer&&window.clearTimeout(this._decorStylePersistTimer),this._decorStylePersistTimer=window.setTimeout(()=>{this._decorStylePersistTimer=null,this._persistDecorStyle()},1e3)}_persistDecorStyle(){const t=this.host._serverCfg;if(!t)return;const e=Ks(this.host._decorStyle),s={...t.settings},i=JSON.stringify(s.decor_default_style??null);e?s.decor_default_style=e:delete s.decor_default_style,JSON.stringify(s.decor_default_style??null)!==i&&(this.host._serverCfg={...t,settings:s},this._saveConfig())}async _saveSettingsDialog(){const t=this.host._settingsDialog;if(!t||t.busy)return;this.host._settingsDialog={...t,busy:!0};let e=null;try{const s=this.host._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Vs);let o={...s.settings};i?delete o.fill_colors:o.fill_colors=t.colors;const a=this.host._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(a)&&a>0&&300!==Math.round(a)?o.glow_radius_cm=Math.round(a):delete o.glow_radius_cm,t.bgColor?o.bg_color=t.bgColor:delete o.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?o.north_deg=t.northDeg:delete o.north_deg,o.bg_mode=t.bgMode,t.sunRays?o.sun_rays=!0:delete o.sun_rays,t.showRoomTooltip?delete o.show_room_tooltip:o.show_room_tooltip=!1,o=Js(o,t.zigbeeTopology),delete o.weather_entity;const n={...s,settings:o};e=u(s,n,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=n,await this._saveConfigNow(e),!t.showRoomTooltip&&this.host._tip?.room&&(this.host._tip=null),this.host._settingsDialog=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.saved"))}catch(t){e&&m(this.host,e,_),this.host._settingsDialog&&(this.host._settingsDialog={...this.host._settingsDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_boolInput(t,e,s=!1){const i=t=>e(!!t.target.checked);return customElements.get("ha-switch")?p``:p``}_rangeInput(t,e,s,i,o,a=!1,n){const r=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?p``:p``}_renderColorRow(t,e){const s=this.host._settingsDialog.colors[t];return p`
    - {this._setFillColor(t,{c:e.detail.color,a:e.detail.opacity})}}> -
    `}_renderAlignDialog(){const t=this.host._alignDialog,e=t.report,s=t.changed&&!t.preflight?.ok,i=t.preflight?.failures||[],o=i.slice(0,3).map(t=>t.displayName),a=o.length?o.join(", "):this.host._t("gs.align_preflight_space",{n:"1"}),n=Math.max(0,i.length-o.length),r=n?this.host._t("gs.align_preflight_more",{n:String(n)}):"",l=e.spaceRefsRemapped+e.roomRefsRemapped+e.positionsRemapped+e.markersDetached,h=e.migrated+e.canonicalized+e.coordsCanonicalized+e.wallSegmentsMigrated+e.wallsMerged+e.spansMerged+e.partitionsMerged+e.partitionsReconciled+e.openingsRehosted+e.redundantDraftsRemoved,c=e.moved+e.rotated+e.removedDrafts+e.coordsCanonicalized+e.wallsStraightened,u=Math.ceil(10*e.maxStraightenShiftCm)/10,_=(this.host._serverCfg?.spaces||[]).find(t=>String(t?.id||"")===e.maxStraightenSpace),m=(this.host._serverCfg?.spaces||[]).length>1&&_?String(_.title||_.id):"",g=e.orphanRoomLabelsRemoved+e.orphanDevicePositionsRemoved+e.orphanGroupPositionsRemoved,f=e.liveMissingPositions.map(t=>t.name).filter(Boolean),b=f.slice(0,3).join(", "),v=Math.max(0,f.length-3),y=b?this.host._t("gs.optimize_live_names",{names:b,more:v?this.host._t("gs.optimize_reference_more",{n:String(v)}):""}):"",$=e.unverifiedPositions.some(t=>"registry_unavailable"===t.reason),w=t=>e.removedPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_removed"):e.liveMissingPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_live"):this.host._t("gs.optimize_detail_unverified"),k=t=>this.host._t("room_label"===t?"gs.optimize_detail_room_label":"group"===t?"gs.optimize_detail_group":"device"===t?"gs.optimize_detail_device":"gs.optimize_detail_unknown"),x=[...e.removedPositions,...e.liveMissingPositions.filter(t=>!e.removedPositions.some(e=>e.id===t.id)),...e.unverifiedPositions],S=x.slice(0,10),M=Math.max(0,x.length-S.length);return p`{this.host._alignDialog=null,this.host._preflightClipboardFallback=null}}> -
    - ${s?p` -

    ${this.host._t("gs.align_preflight_failed",{spaces:a,more:r})}

    - ${i.slice(0,10).map(t=>p`

    - ${t.displayName}: ${this.host._t(`gs.preflight_reason_${t.reason}`)} -

    `)} - ${i.length>10?p`

    - ${this.host._t("gs.align_preflight_more",{n:String(i.length-10)})} -

    `:d} -
    ${this.host._t("gs.align_preflight_hint")}
    - ${this._preflightVersionsDiffer()?p` -
    ${this.host._t("gs.preflight_update_hint")}
    `:d} -
    - -
    - ${this.host._preflightClipboardFallback?p`
    - ${this.host._t("gs.preflight_copy")} -
    ${this.host._preflightClipboardFallback}
    -
    `:d}`:t.changed?p` - ${e.moved?p`

    ${this.host._t("gs.align_count",{n:String(e.moved),total:String(e.total),cm:String(t.cm)})}

    `:d} - ${e.latticeCoordinatesCanonicalized?p` -

    ${this.host._t("gs.optimize_lattice_summary",{n:String(e.latticeCoordinatesCanonicalized),cm:Ys(e.latticeMaxShiftCm)})}

    - ${e.latticeSpaces.map(t=>p`

    ${this.host._t("gs.optimize_lattice_space",{space:t.space,n:String(t.canonicalized),far:String(t.far)})}

    `)} - `:d} - ${t.where?p`

    ${this.host._t("gs.align_where",{s:t.where})}

    `:d} - ${e.rotated?p`

    ${this.host._t("gs.align_turned",{n:String(e.rotated)})}

    `:d} - ${e.removedDrafts?p`

    ${this.host._t("gs.align_removed_drafts",{n:String(e.removedDrafts)})}

    `:d} - ${e.redundantDraftsRemoved?p`

    ${this.host._t("gs.optimize_redundant_drafts",{n:String(e.redundantDraftsRemoved)})}

    `:d} - ${e.wallSegmentsMigrated?p`

    ${this.host._t("gs.wall_segments_migrated",{n:String(e.wallSegmentsMigrated)})}

    `:d} - ${e.legacyZeroWallsMigrated?p`

    ${this.host._t("gs.zero_walls_migrated",{n:String(e.legacyZeroWallsMigrated)})}

    `:d} - ${h?p`

    ${this.host._t("gs.optimize_changes",{m:String(e.migrated),c:String(e.canonicalized),p:String(e.coordsCanonicalized),w:String(e.wallsMerged),s:String(e.spansMerged),i:String(e.partitionsMerged)})}

    `:d} - ${e.partitionsReconciled?p`

    ${this.host._t("gs.optimize_coincident_partitions",{n:String(e.partitionsReconciled)})}

    `:d} - ${e.openingsRehosted?p`

    ${this.host._t("gs.optimize_openings_rehosted",{n:String(e.openingsRehosted)})}

    `:d} - ${e.wallsStraightened?p`

    ${this.host._t("gs.optimize_walls_straightened",{n:String(e.wallsStraightened),cm:String(u)})}

    `:d} - ${m?p`

    ${this.host._t("gs.optimize_walls_straightened_where",{s:m})}

    `:d} - ${e.glowSpacesMigrated||e.glowRoomsMigrated?p`

    ${this.host._t("gs.optimize_glow_migration",{spaces:String(e.glowSpacesMigrated),rooms:String(e.glowRoomsMigrated)})}

    `:d} - ${c?p`
    ${this.host._t("gs.align_warn")}
    `:d}`:p`

    ${this.host._t(e.liveMissingPositions.length||e.unverifiedPositions.length||e.nestedRefsUnresolved?"gs.optimize_no_automatic_changes":"gs.align_none")}

    `} - ${!s&&e.wallsStraightenSkipped?p`

    ${this.host._t("gs.optimize_walls_straighten_skipped",{n:String(e.wallsStraightenSkipped)})}

    `:d} - ${l?p`

    ${this.host._t("gs.optimize_references",{spaces:String(e.spaceRefsRemapped),rooms:String(e.roomRefsRemapped),positions:String(e.positionsRemapped),detached:String(e.markersDetached)})}

    `:d} - ${g?p`

    ${this.host._t("gs.optimize_orphans_removed",{total:String(g),rooms:String(e.orphanRoomLabelsRemoved),devices:String(e.orphanDevicePositionsRemoved),groups:String(e.orphanGroupPositionsRemoved)})}

    `:d} - ${e.liveMissingPositions.length?p`
    -

    ${this.host._t(t.removeLiveMissingPositions?"gs.optimize_live_positions_remove":"gs.optimize_live_positions",{n:String(e.liveMissingPositions.length),names:y})}

    - - ${t.removeLiveMissingPositions?p`
    - ${this.host._t("gs.optimize_live_selected")} -
    `:d} -
    `:d} - ${e.unverifiedPositions.length?p``:d} - ${e.nestedRefsUnresolved?p``:d} - ${x.length?p`
    - ${this.host._t("gs.optimize_details")} -
      - ${S.map(t=>p`
    • ${this.host._t("gs.optimize_detail_item",{status:w(t),kind:k(t.kind),id:t.id,space:t.spaceId})}
    • `)} -
    - ${M?p`
    ${this.host._t("gs.optimize_details_more",{n:String(M)})}
    `:d} -
    `:d} -
    -
    - - - ${t.changed&&t.preflight?.ok?p` - `:d} -
    -
    `}_renderSettingsDialog(){return p`this.host._settingsDialog=null}> -
    -
    ${vo(l(this.host.hass,this.host._config?.language),"gs.hint")}
    - this.host._settingsDialog={...this.host._settingsDialog,zigbeeTopology:t.detail}}> - - ${this._renderColorRow("light_on","gs.light_on")} - ${this._renderColorRow("light_off","gs.light_off")} - ${this._renderColorRow("light_none","gs.light_none")} - - ${this._renderColorRow("temp_cold","gs.temp_cold")} - ${this._renderColorRow("temp_ok","gs.temp_ok")} - ${this._renderColorRow("temp_hot","gs.temp_hot")} - - ${this._renderColorRow("lqi_low","gs.lqi_low")} - ${this._renderColorRow("lqi_high","gs.lqi_high")} - - ${this._renderColorRow("glow_base","gs.glow_base")} - ${this._renderColorRow("glow_light","gs.glow_light")} -
    - - ${this._help("gs.glow_radius.help")} - {const e=nh(t.target.value);null!=e&&e>0&&(this.host._settingsDialog={...this.host._settingsDialog,glowRadius:e})}} /> - ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} -
    - - ${this._renderColorRow("wall_fill","gs.wall_fill")} - -
    - - ${this._help("gs.bg_mode.help")} - -
    - ${"static"===this.host._settingsDialog.bgMode?p`
    - {this.host._settingsDialog={...this.host._settingsDialog,bgColor:t.detail.color}}}> - ${this.host._settingsDialog.bgColor?p``:p`${this.host._t("gs.bg_theme")}`} -
    `:d} - - ${Zs(this.host.hass)?d:p`
    ${this.host._t("gs.sun_missing")}
    `} -
    - ${this.host._renderCompass()} -
    -
    - - ${this._help("gs.north.help")} -
    -
    - {const e=t.target.value.trim(),s=""===e?null:Math.round(Number(e));this.host._settingsDialog={...this.host._settingsDialog,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} /> - ${null!==this.host._settingsDialog.northDeg?p``:d} -
    -
    -
    - - ${this.host._canEdit?p` - -
    ${this.host._t("gs.backup_hint")}
    -
    - - - - this._pickBackupImport(t)} /> - - ${this.host._canOptimizeUndo&&"import"===this.host._undoKind?p` - `:d} -
    `:d} - -
    ${this.host._t("gs.grid_hint")}
    -
    - -
    - ${this.host._canOptimizeUndo&&"import"!==this.host._undoKind?p`
    - -
    `:d} -
    -
    - - - - -
    -
    `}_rulesSet(t){this.host._rulesDialog={...this.host._rulesDialog,rules:t}}async _saveRules(){const t=this.host._rulesDialog;if(!t||t.busy)return;const e=t.rules.filter(t=>t.pattern.trim()&&t.icon.trim());this.host._rulesDialog={...t,busy:!0};try{const t=this.host._serverCfg,s=JSON.stringify(e)===JSON.stringify(Qt),i={...t.settings};s?delete i.icon_rules:i.icon_rules=e,this.host._serverCfg={...t,settings:i},await this._saveConfigNow(),this.host._rulesDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("rules.saved"))}catch(t){this.host._rulesDialog&&(this.host._rulesDialog={...this.host._rulesDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_renderRulesDialog(){const t=this.host._rulesDialog,e=Xs(t.rules),s=t.test.trim()?Qs(t.test,"",e):null,i=(e,s)=>{const i=[...t.rules],o=e+s;o<0||o>=i.length||([i[e],i[o]]=[i[o],i[e]],this._rulesSet(i))};return p`this.host._rulesDialog=null}> -
    -
    ${this.host._t("rules.hint")}
    -
    - this.host._rulesDialog={...t,test:e.target.value}} /> - ${s?p`${s}`:d} -
    - ${t.rules.map((e,s)=>{const o=""!==e.pattern.trim()&&!ti(e.pattern);return p`
    - {const o=[...t.rules];o[s]={...e,pattern:i.target.value},this._rulesSet(o)}} /> - {const o=[...t.rules];o[s]={...e,icon:i.target.value},this._rulesSet(o)}} /> - - i(s,-1)}> - i(s,1)}> - this._rulesSet(t.rules.filter((t,e)=>e!==s))}> -
    `})} - -
    -
    - - - - -
    -
    `}_saveKioskScale(t){this.host._kioskScale={...this.host._kioskScale,...t};try{localStorage.setItem("houseplan_card_kiosk_v1",JSON.stringify(this.host._kioskScale))}catch{}this.host.requestUpdate()}_renderKioskDialog(){const t=this.host._kioskScale,e=(e,s)=>p` -
    - ${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))} - ${Math.round(100*t[e])}% -
    `;return p`this.host._kioskDialog=!1}> -
    -
    ${this.host._t("kiosk.hint")}
    - ${e("icon",this.host._t("kiosk.icon_scale"))} - ${e("font",this.host._t("kiosk.font_scale"))} -
    -
    - - - -
    -
    `}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return d;const s=e.marker?.vacuum||{},o=this.host._vacAllCamerasFor===e.id,a=this.host._vacSourceResolution(e,o),n=a.entityId,r=a.candidates.find(t=>t.entityId===n)||null,l=n?ei(this.host.hass?.states[n]?.attributes):null,h=!1!==s.live&&("ok"===a.status||"unsupported"===a.status),c=this._vacPlanRoomAnchors(e.space),u=l?si(l.rooms,c.map(t=>t.name)):0,_=!!(h&&l&&u>=3),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=a.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=o?a.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>p` - `,$=a.candidates.some(t=>g.has(t.entityId)&&"known_xcme_incomplete"===t.category)||!!a.pinned&&!!n?.startsWith("camera.")&&!r?.hasPosition;return p` - -
    -
    -
    ${this.host._t("vac.diag_source")}${n||this.host._t("vac.source_none")}
    - ${r?.platform?p`
    ${this.host._t("vac.diag_platform")}${r.platform}
    `:d} -
    ${this.host._t("vac.diag_status")}${this.host._t(`vac.source_status_${a.status}`)}
    -
    ${this.host._t("vac.diag_position")}${l?.pos?this.host._t("common.yes"):this.host._t("common.no")}
    -
    ${this.host._t("vac.diag_rooms")}${i(this.host._t("vac.diag_rooms_value"),{total:String(l?.rooms.length||0),matched:String(u),readiness:this.host._t(u>=3?"vac.autocal_ready":"vac.autocal_not_ready")})}
    -
    ${this.host._t("vac.diag_path")}${l?.path.length?this.host._t("common.yes"):this.host._t("common.no")}
    -
    ${this.host._t("vac.diag_map")}${l?.mapId??"default"}
    -
    - ${"missing"===a.status||"disabled"===a.status||"unverified"===a.status?p`
    - ${this.host._t(`vac.source_banner_${a.status}`)} - -
    `:d} - ${$?p`
    - ${this.host._t("vac.xcme_hint")} -
    attributes:
    -  - vacuum_position
    -  - rooms
    -  - path
    -  - map_name
    -
    `:d} -
    - ${this.host._t("vac.choose_source")} -
    - - ${f.map(y)} -
    {t.currentTarget.open?this.host._vacOpenAllCameras(e):(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null)}}> - ${this.host._t("vac.all_cameras")} -
    ${this.host._t("vac.all_cameras_warn")}
    - ${o?b.length?b.map(y):p`
    ${this.host._t("vac.all_cameras_empty")}
    `:d} -
    -
    -
    -
    - ${_?p``:d} - ${h?p``:d} - ${this.host._t("vac.documentation")} -
    - ${l?p` - - - - ${Mo(this,e,a)} - `:d} -
    `}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i,o=""){return Pl(this,t,e,s,i,o)}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:V(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=oi(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}async _vacAutoCalibrate(t){if(this.host._markerDialog?.busy)return;const e=this.host._vacSource(t),s=e?ei(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacMapId(t,s),a=function(t,e,s,i,a){const n=o(t,e,s,i).find(t=>t.source===i&&t.map_id===a);return{space:n?.space||s,routeId:n?.id||""}}(t.id,t.marker?.vacuum??null,t.space,e,i),n=ai(s.rooms,this._vacPlanRoomAnchors(a.space));if(!n)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const r=this.host._serverCfg?.spaces?.find(t=>t.id===a.space),l=Number(r?.cell_cm),h=Number.isFinite(l)&&l>0?l:5,c=ni(n.residual,this.host._gridPitch,h);c>ri?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length,error:Se(c,"mi"===this.host.hass?.config?.unit_system?.length)}:await async function(t,e){if(Dl.has(t))return;Dl.add(t);const s=t.host,i=s._markerDialog,o=i?{...i,busy:!0}:null;o&&(s._markerDialog=o,s.requestUpdate());try{await Pl(t,e.markerId,e.source,e.mapId,e.matrix,e.routeId)&&s._showToast(s._t("vac.autocal_done",{rooms:String(e.rooms)}))}finally{Dl.delete(t),o&&s._markerDialog===o&&(s._markerDialog={...o,busy:!1},s.requestUpdate())}}(this,{markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length})}_vacApplyCalibrationProposal(t){return async function(t,e){const s=t.host,i=s._vacCalConfirm;if(!i||i.busy)return;if(e){const t=s._devices.find(t=>t.id===i.markerId),e=n(i.matrix);if(!t||!e)return;const o=i.space||t.space;if(o!==s._space&&!s._commitSpace(o))return;return s._vacCalConfirm=null,s._markerDialog=null,void(s._vacFit={markerId:i.markerId,source:i.source,routeId:i.routeId,mapId:i.mapId,p:e,drag:null})}const o={...i,busy:!0};s._vacCalConfirm=o,s.requestUpdate();const a=await Pl(t,i.markerId,i.source,i.mapId,i.matrix,i.routeId);s._vacCalConfirm===o&&(s._vacCalConfirm=a?null:{...i,busy:!1},s.requestUpdate()),a&&s._showToast(s._t("vac.autocal_done",{rooms:String(i.rooms)}))}(this,t)}_vacStartFit(t,e=""){if(this.host._markerDialog?.busy)return;const s=this.host._vacSource(t),i=s?ei(this.host.hass?.states[s]?.attributes):null;if(!s||!i)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const l=this._vacMapId(t,i),h=function(t,e,s){const i=o(t,e,s.dockSpace,s.source),l=i.find(t=>t.id===s.routeId)||i.find(t=>t.source===s.source&&t.map_id===s.mapId)||null,h=l?.space||s.dockSpace,c=s.viewBoxOf(h);if(!c)return null;const d=a(l?l.calibration:e?.calibration?.[s.mapId]),p=d&&n(d)||r(s.rooms,c);return{space:h,routeId:l?.id||"",params:p}}(t.id,t.marker?.vacuum??null,{routeId:e,source:s,mapId:l,dockSpace:t.space,rooms:i.rooms,viewBoxOf:t=>this.host._spaceModelById(t)?.vb??null});h&&(this.host._markerDialog=null,(h.space===this.host._space||this.host._commitSpace(h.space))&&(this.host._vacFit={markerId:t.id,source:s,mapId:l,routeId:h.routeId,p:h.params,drag:null}))}_vacFitSave(){return async function(t,e){const s=t.host,i=s._vacFit;if(!i||i.busy)return;const o={...i,busy:!0,drag:null};s._vacFit=o,s.requestUpdate();const a=await Pl(t,i.markerId,i.source,i.mapId,e(i.p),i.routeId);s._vacFit===o&&(s._vacFit=a?null:{...i,busy:!1,drag:null},s.requestUpdate()),a&&s._showToast(s._t("vac.cal_done"))}(this,hi)}_vacFitTurn(t){const e=this.host._vacFit;if(!e||e.busy)return;const s=ei(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:li(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),s.busy)return;if("pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=ei(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=hi(i.p0),[r,l]=ci(n,e[0],e[1]),[h,c]=ci(n,i.fx,i.fy),d=Math.hypot(r-h,l-c)||1,[p,u]=[2*r-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*a-c)/2,m=Math.max(.05,_/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:li(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return d;const s=ei(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return d;const i=hi(e.p),o=[],a=[],n=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>ci(i,t,e));e.forEach(([t,e])=>{a.push(t),n.push(e)});const[s,r]=ci(i,t.cx,t.cy);o.push(Rt` - ${t.name}`)}let r=d;if(s.pos){const[e,o]=ci(i,s.pos.x,s.pos.y);r=Rt``}const l=[];if(a.length){const e=(()=>{const t=i[0]*i[4]-i[1]*i[3];return(e,s)=>[(i[4]*(e-i[2])-i[1]*(s-i[5]))/t,(-i[3]*(e-i[2])+i[0]*(s-i[5]))/t]})(),s=Math.min(...a),o=Math.max(...a),r=Math.min(...n),h=Math.max(...n),c=.022*t.w,d=c/4;for(const[t,i,a,n]of[[s,r,o,h],[o,r,s,h],[o,h,s,r],[s,h,o,r]]){const s=e(a,n);l.push(Rt` - `)}}return p`this._vacFitPointer(e,t)} - @pointermove=${e=>this._vacFitPointer(e,t)} - @pointerup=${e=>this._vacFitPointer(e,t)} - @pointercancel=${e=>this._vacFitPointer(e,t)}>${o}${r}${l}`}_resetRoomDialogFields(){this.host._roomEditId=null,this.host._roomFill="",this.host._roomCustomFill=null,this.host._roomTempSrc="",this.host._roomHumSrc="",this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomNameScale=1,this.host._roomLabelScale=1}_openRoomEdit(t){if(!t.id)return;this.host._roomEditId=t.id,this.host._nameSel=t.name||"",this.host._areaSel=t.area||"",this.host._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this.host._roomCustomFill=e&&"object"==typeof e?Ns(e,Fs(this.host._curSpaceCfg).customFill):null,this.host._roomTempSrc=t.settings?.temp_source||"",this.host._roomHumSrc=t.settings?.hum_source||"",this.host._roomNameScale=di(t.settings?.name_scale),this.host._roomLabelScale=di(t.settings?.label_scale),this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this.host._roomFill&&(t.fill_mode=this.host._roomFill),this.host._roomCustomFill&&(t.custom_fill=this.host._roomCustomFill),this.host._roomTempSrc&&(t.temp_source=this.host._roomTempSrc),this.host._roomHumSrc&&(t.hum_source=this.host._roomHumSrc),1!==this.host._roomNameScale&&(t.name_scale=this.host._roomNameScale),1!==this.host._roomLabelScale&&(t.label_scale=this.host._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this.host._curSpaceCfg,e=t?.rooms.find(t=>t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=O(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(B(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*lh,y:s.y*lh};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},ch(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),l=this.host._viewOr(n),h=(t.clientX-this.host._drag.sx)/r.width*l.w,c=(t.clientY-this.host._drag.sy)/r.height*l.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=ie(this.host._drag.ox+h),p=ie(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},ch(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/lh,y:a.y/lh,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return d;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=Te(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return d;const o=(i[0]-s.x)/s.w*100,a=(i[1]-s.y)/s.h*100;return p``}_alignCandidates(){const t=[],e=this.host._spaceModel();if(this.host._markup){if(!e)return t;if(this.host._drag?.id.startsWith("rl_")){const s=this.host._drag.id.slice(3);for(const i of e.rooms){if(!i.name||i.id===s)continue;const e=this._labelPos(i,this.host._space);t.push([e.x,e.y])}return t}for(const s of e.rooms){const e=V(s);if(e)for(const s of e)t.push(s)}if("draw"===this.host._tool)for(const e of this.host._path)t.push(e);if("split"===this.host._tool&&this.host._splitSel?.pts)for(const e of this.host._splitSel.pts)t.push(e);return t}if("devices"===this.host._mode){const e=this.host._deviceDrag?.id??this.host._drag?.id;for(const s of this.host._devices){if(s.space!==this.host._space||s.id===e||"ha_disabled"===s.bindingStatus?.kind)continue;const i=this.host._pos(s);t.push([i.x,i.y])}return t}if("decor"===this.host._mode){const e=this.host._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this.host._decorDraft&&t.push(this.host._decorDraft.a),t}return t}_renderAlignGuides(){const t=this.host._alignPoint;if(!t)return Rt``;const e=this.host._drag?.id.startsWith("rl_")?.5*this.host._gridPitch:.05*this.host._gridPitch,s=pi(t,this._alignCandidates(),e);if(!s.length)return Rt``;const i=this.host._gridPitch,o=1.5*i;return Rt` - ${s.map(e=>{const[s,a,n,r]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return Rt` - `})} - `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180,s=is(15,this.host._cellCm);return Rt``}_renderOpeningDimensionGuides(t){const e=t.labels.flatMap(t=>t.dimension?[t.dimension]:[]);if(!e.length)return Rt``;const s=this._cssPxToRender(4);return Rt``}_renderOpeningPlacementPreview(){const t=this.host._openingPreview;if(!t)return Rt``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:ui(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:t.face},s="passage"===t.type?_i(t,is(this.host._gridPitch,this.host._cellCm)):null;return Rt` - `}_renderOpeningDialog(){const t=this.host._openingDialog,e=this.host._spaceModel()?.partitions||[],s=t.id?this.host._curSpaceCfg?.openings?.find(e=>e.id===t.id):null,i={id:t.id||"preview",type:t.type,x:t.x/lh,y:t.y/this.host._spaceH,angle:t.angle,length:s&&!t.lengthTouched?s.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/lh,...t.host?{host:t.host}:{}},o=us(s,i),a=t.host?o?it(i,e,lh,this.host._cellCm,this.host._gridPitch):J(i,e,lh,this.host._cellCm,this.host._gridPitch):null,n="does-not-fit-jamb"===a?.reason,r=t.host?e.find(e=>e.id===t.host.id):null,l=r?Se(r.cm/2,this.host._imperial):"",h=!!t.host&&!a?.resolved&&!n,c="gate"===t.type?"mdi:gate":"window"===t.type?"mdi:window-closed-variant":"passage"===t.type?"mdi:arch":"mdi:door",u=(e,s)=>{const i="contact"===e?t.contact:t.lock,o="contact"===e?!!t.contactOpen:!!t.lockOpen,a="contact"===e?t.contactFilter||"":t.lockFilter||"",n=s.find(t=>t.value===i),r=n?.label||this.host.hass.states[i]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[i]?.name||i,l=Ji(s,a);return p` - - ${o?p`
    - this._filterOpeningEntities(e,t.target.value)} /> -
    - - ${l.map(t=>p` - `)} - ${l.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} -
    -
    `:d}`};return p`this.host._openingDialog=null}> -
    - ${t.host?p` -
    - ${h||n?p``:d} - ${h?this.host._t("opening.partition_orphan"):n?this.host._t("opening.partition_jamb_margin",{distance:l}):this.host._t("opening.host_partition")} -
    `:d} - - - - - - - - {const s=nh(e.target.value);null!=s&&(this.host._openingDialog={...t,lengthCm:s,lengthTouched:!0})}} /> - - ${"passage"===t.type&&(t.contact||t.lock)?p`
    - - ${this.host._t("opening.passage_binding_warning")} -
    `:d} - - ${"passage"!==t.type?p` - ${u("contact",this._contactCandidates())} - ${t.contact?p``:d}`:d} - - ${"door"===t.type||"gate"===t.type?p` - ${u("lock",this._lockCandidates())}`:d} - - ${"gate"!==t.type&&"passage"!==t.type?p``:d} - ${"passage"!==t.type?p``:d} -
    - -
    `}_gridLevels(){const t=this.host._stageEl,e=this.host._viewOr(this.host._baseVb()),s=t&&t.clientWidth&&e.w?t.clientWidth/e.w:1;return fi(this.host._gridPitch,s)}_renderMarkupDefs(t){const e=this._gridLevels();if(!e)return Rt``;const s=this.host._gridPitch*e.fine,i=this.host._gridPitch*e.coarse,o=this.host._gridPitch*e.fine*.14;return Rt` - - - - - - - - - - - - - - `}_renderPhysicalEditorLayer(){const t=this.host._spaceModel();if(!t)return Rt``;const e=this.host._gridPitch,s=this.host._viewOr(this.host._baseVb()),i=this.host._stageEl,o=i?.clientWidth?s.w/i.clientWidth:e/8,a=Math.max(.22*e,8*o),n=Math.max(a,12*o),r=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`,l=(t,e)=>this.host._physicalSel?.kind===t&&this.host._physicalSel.id===e||"column"===t&&this.host._duplicateColumnId===e,h=(t.room_drafts||[]).flatMap(t=>t.points.slice(0,-1).map((e,s)=>{const i=t.points[s+1],a=Number(t.segments[s]?.cm),n=nt(Number.isFinite(a)?a:15,this.host._cellCm,this.host._gridPitch);return Rt`{this.host._physicalSel={kind:"draft",id:t.id,segment:s},this._registerPhysicalTap("draft",t.id,s)}}>`})),c=(t.partitions||[]).map(t=>{const e=$e(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);return e?Rt`this._physicalDown(e,"partition",t.id)} - @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`:Rt`this._physicalDown(e,"partition",t.id)} - @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`}),p=(t.wall_columns||[]).map(t=>{const e=this.host._physicalRotate?.id===t.id?{...t,angle:this.host._physicalRotate.angle}:t,s=ye(e,this.host._cellCm,this.host._gridPitch);return Rt`this._physicalDown(e,"column",t.id)} - @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`}),u=this.host._physicalDrag,_=(()=>{if(!u?.moved)return d;if("partition"===u.kind&&0===Number(u.base.cm)){const t=u.base;return Rt``}const t="partition"===u.kind?$e(u.base.a,u.base.b,u.base.cm,this.host._cellCm,this.host._gridPitch):ye(u.base,this.host._cellCm,this.host._gridPitch);return t?Rt``:d})(),m=(()=>{const s=this.host._physicalSel;if(!s)return d;if("draft"===s.kind){const e=t.room_drafts.find(t=>t.id===s.id);return e?Rt` - t.join(",")).join(" ")}> - ${e.points.map(t=>Rt``)} - `:d}if("partition"===s.kind){const e=t.partitions.find(t=>t.id===s.id);if(!e)return d;const i=$e(e.a,e.b,e.cm,this.host._cellCm,this.host._gridPitch),o=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2;return Rt` - ${i?Rt``:Rt``} - - - - `}const i=t.wall_columns.find(t=>t.id===s.id);if(!i)return d;const l=this.host._physicalRotate?.id===i.id?{...i,angle:this.host._physicalRotate.angle}:i,h=ye(l,this.host._cellCm,this.host._gridPitch);if("square"!==l.shape)return Rt` - - - `;const c=nt(l.cm,this.host._cellCm,this.host._gridPitch),p=re(l.angle)*Math.PI/180,u=Math.sin(p),_=-Math.cos(p),m=[l.center[0]+u*c/2,l.center[1]+_*c/2],g=[m[0]+u*Math.max(e,24*o),m[1]+_*Math.max(e,24*o)];return Rt` - - - - this._physicalRotateDown(t,i)} - @pointermove=${t=>this._physicalRotateMove(t)} - @pointerup=${t=>this._physicalRotateUp(t)}> - `})();return Rt`${h}${c}${p}${_}${m}`}_renderHiddenWallDiagnosticOverlay(){if(!this.host._markup)return Rt``;const t=this._hiddenWallDiagnosticSnapshot().value;if(!t.segments.length)return Rt``;const e=nt(5,this.host._cellCm,this.host._gridPitch);return Rt``}_renderPlanSnapOverlay(){if(!this.host._markup)return Rt``;const t=this._planSnapGeometrySnapshot().value,e=this.host._activePlanSnapCandidate,s=new Set(this.host._activePlanSnapConflicts.map(t=>t.key)),i=nt(5,this.host._cellCm,this.host._gridPitch),o=nt(10,this.host._cellCm,this.host._gridPitch);return Rt``}_syncPlanSnapActiveMarker(t){const e=this.host.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(e){if(e.setAttribute("class",`plan-snap-node${t?" active":""}${"line"===t?.kind?" dynamic":""}`),e.setAttribute("data-active",t?"true":"false"),e.setAttribute("visibility",t?"visible":"hidden"),!t)return e.removeAttribute("data-kind"),void e.removeAttribute("data-key");e.setAttribute("data-kind",t.kind),e.setAttribute("data-key",t.key),e.setAttribute("cx",String(t.point[0])),e.setAttribute("cy",String(t.point[1])),e.setAttribute("r",String(nt(10,this.host._cellCm,this.host._gridPitch)))}}_syncPlanSnapConflictMarkers(t){const e=new Set(t.map(t=>t.key));for(const t of this.host.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])t.setAttribute("class","plan-snap-node"+(e.has(t.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(t){let e=0;const s=this.host._spaceModel();if(!s)return null;if("partition"===t.sourceKind)e=Number(s.partitions.find(e=>e.id===t.sourceId)?.cm)||0;else if("draft"===t.sourceKind){const i=/^(.*):(\d+)$/.exec(t.sourceId),o=i?s.room_drafts.find(t=>t.id===i[1]):null;e=o&&i&&Number(o.segments[Number(i[2])]?.cm)||0}else e=bi(s.rooms,this.host._spaceWalls,this.host._openCuts(),[t.a[0],t.a[1],t.b[0],t.b[1]],this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh);return e>0?{a:[...t.a],b:[...t.b],halfDepth:nt(e,this.host._cellCm,this.host._gridPitch)/2}:null}_drawPreviewJoinPatchD(t,e){if(t.length<2)return"";const s=[];for(let i=0;i+10&&s.push({a:t[i],b:t[i+1],halfDepth:e[i]});if(!s.length)return"";const i=2e-4*this.host._gridPitch,o=this._planSnapGeometrySnapshot().value.segments.filter(e=>t.some(t=>G(t,[e.a[0],e.a[1],e.b[0],e.b[1]])<=i)).map(t=>this._planSnapPhysicalSegment(t)).filter(t=>!!t);return vi([...s,...o],i).map(t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(t){const e=this.host._openCuts(),s=this.host._thickWallCuts(),i=e.concat(s),o=i.length?j(this.host._segments,i,.02*this.host._gridPitch):this.host._segments,a=this.host._path,n=this.host._gridPitch,r=this.host._viewOr(this.host._baseVb()),l="draw"===this.host._tool?this.host._drawWallCm:null,h=(()=>"draw"===this.host._tool&&a.length&&null!=l?this.host._contourClosed?a:this.host._cursorPt?[...a,this.host._cursorPt]:a.length>=2?a:null:null)(),c=h?xn(h.length-1,this.host._contourClosed?[...this.host._draftSegmentCms,this.host._closingWallCm??void 0]:this.host._draftSegmentCms,l,X).map(t=>nt(t,this.host._cellCm,this.host._gridPitch)/2):[],p=h?yi(h,nt(l,this.host._cellCm,this.host._gridPitch)/2,this.host._contourClosed,c):"",u=h?this._drawPreviewJoinPatchD(h,c):"",_=this.host._wallFaceBatch?.candidates[this.host._wallFaceBatch.index]?.repair||this.host._wallRepairDiagnostic;return Rt` - ${this._gridLevels()?Rt``:d} - ${o.map(t=>Rt``)} - ${this._renderPhysicalEditorLayer()} - ${"column"===this.host._tool&&this.host._cursorPt&&this.host._drawWallCm?Rt`{const t={shape:"square",center:this.host._cursorPt,cm:this.host._drawWallCm};return`M ${ye(t,this.host._cellCm,this.host._gridPitch).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`})()}>`:d} - ${p?Rt` - `:d} - ${h&&0===l?Rt`t.join(",")).join(" ")}>`:d} - ${u?Rt` - `:d} - ${_?Rt``:d} - ${"split"===this.host._tool&&this.host._splitSel?.pts?.length?Rt`${this.host._splitSel.pts.length>1?Rt``:d} - ${this.host._splitSel.pts.map((t,e)=>Rt``)} - ${this.host._cursorPt?Rt``:d}`:d} - `}_renderActiveChainInk(){const t=this.host._path,e=this.host._gridPitch;return Rt` - ${t.length>1?Rt``:d} - ${t.length&&this.host._cursorPt&&"draw"===this.host._tool&&!this.host._contourClosed?Rt` - ${this.host._activePlanSnapCandidate||this.host._activePlanSnapConflicts.length?d:Rt``}`:d} - ${t.map((t,s)=>Rt``)} - `}_renderPartitionDeleteDialog(){const t=this.host._partitionDeleteDialog,e="mi"===this.host.hass?.config?.unit_system?.length;return p`this.host._partitionDeleteDialog=null}> -
    -

    ${this.host._t("confirm.delete_partition_openings_body",{count:t.openings.length})}

    -
      - ${t.openings.map(t=>p`
    • ${this.host._t("confirm.delete_partition_openings_item",{type:this.host._t(`opening.${t.type}`),length:Se(t.length*lh/this.host._gridPitch*this.host._cellCm,e)})}
    • `)} -
    -
    -
    - - - -
    -
    `}_renderRoomDeleteDialog(){const t=this.host._roomDeleteDialog;return p`this.host._roomDeleteDialog=null}> -
    -

    ${this.host._t("confirm.delete_room_body")}

    -
    -
    - - - - -
    -
    `}_renderPhysicalDialog(){const t=this.host._physicalDialog,e="column"===t.kind;return p`this.host._physicalDialog=null}> -
    - ${e?p` - `:d} - -
    this.host._physicalDialog={...t,cm:e.target.value}} /> - ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    - ${e&&"square"===t.shape?p` - - this.host._physicalDialog={...t,angle:e.target.value}} />`:d} - ${t.length?p`
    ${this.host._t("physical.length")}: ${t.length}
    `:d} -
    - -
    `}_renderMarkupBar(){const t=this.host._geometryHistory.undoName,e=this.host._geometryHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    -
    - - - - - - - - - - ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} - - - - -
    -
    - -
    -
    `}_renderDevicesBar(){const t=this.host._devicePositionHistory.undoName,e=this.host._devicePositionHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    -
    - - - - - ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} -
    -
    - - - -
    -
    `}_discoveryFilterState(t){const e=this.host._settings,s=void 0!==t.draftGroupLights?t.draftGroupLights:!1!==e.group_lights,i=e.exclude_integrations,o=void 0!==t.draftExcluded?null===t.draftExcluded?[...$i]:t.draftExcluded:i?[...i]:[...$i],a=void 0!==t.draftExcluded?null===t.draftExcluded:!i,n=void 0!==t.draftGroupLights||void 0!==t.draftExcluded;return{groupLights:s,excluded:o.sort(),usesProductList:a,dirty:n}}_registryIntegrations(){const t=new Set,e=this.host._fullRegistryHass;for(const s of Object.values(e?.entities||{}))s?.platform&&t.add(String(s.platform));for(const s of Object.values(e?.devices||{})){const e=s?.identifiers?.[0],i=Array.isArray(e)?e[0]:null;i&&t.add(String(i))}return[...t].sort()}_discoveryFilterPreview(t){const e=this._discoveryFilterState(t),s=[e.groupLights?1:0,e.usesProductList?"p":e.excluded.join(","),this.host._cfgEpoch,this.host._regSignature].join("|");if(this._discoveryPreviewMemo?.key===s)return this._discoveryPreviewMemo.value;const i={hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),markers:this.host._markers,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||"",loc:t=>this.host._t(t),iconRules:this.host._iconRules},o=(t,e)=>new Set(Yi({...i,settings:t,excluded:e})),a=(t,e)=>new Set(Zi({...i,settings:t,excluded:e}).map(t=>t.bindingRef||t.id)),n=wi(this.host._settings),r={...this.host._settings,group_lights:e.groupLights},l=new Set(e.excluded),h=o(this.host._settings,n),c=o(r,l),d=a(this.host._settings,n),p=a(r,l);let u=0,_=0,m=0;const g=t=>t.includes("light.");for(const t of h)c.has(t)||(u++,g(t)&&m++);for(const t of c)h.has(t)||(_++,g(t)&&m++);for(const t of p)d.has(t)||(u++,g(t)&&m++);for(const t of d)p.has(t)||(_++,g(t)&&m++);const f={appear:u,hide:_,lights:m};return this._discoveryPreviewMemo={key:s,value:f},f}async _saveDiscoveryFilters(t){const e=this.host._serverCfg;if(!e)return;const s=this._discoveryFilterState(t),i={...e.settings};s.groupLights?delete i.group_lights:i.group_lights=!1,s.usesProductList?delete i.exclude_integrations:i.exclude_integrations=s.excluded,this.host._serverCfg={...e,settings:i},this.host._deviceInboxMemo=null,this._discoveryPreviewMemo=null,this.host._regSignature="",this.host._deviceInbox&&(this.host._deviceInbox={...t,draftGroupLights:void 0,draftExcluded:void 0}),this._saveConfig(),this.host._maybeRebuildDevices(),this.host.requestUpdate()}_renderDiscoveryFilters(t){const e=this._discoveryFilterState(t),s=e=>this.host._deviceInbox={...t,...e},i=t.filtersOpen&&e.dirty?this._discoveryFilterPreview(t):null,o=this._registryIntegrations().filter(t=>!e.excluded.includes(t));return p`
    s({filtersOpen:t.target.open})}> - ${this.host._t("device_inbox.filters_title")} - -
    - ${this.host._t("device_inbox.filters_excluded")} -
    - ${e.excluded.map(t=>p`${t} - - `)} -
    - {const i=t.target,o=i.value.trim();o&&!e.excluded.includes(o)&&s({draftExcluded:[...e.excluded,o]}),i.value=""}} /> - - ${o.map(t=>p``)} - - -
    - ${i?p`
    - ${this.host._t("device_inbox.filters_preview_appear",{count:String(i.appear)})} - · ${this.host._t("device_inbox.filters_preview_hide",{count:String(i.hide)})} - · ${this.host._t("device_inbox.filters_preview_lights",{count:String(i.lights)})} -
    `:d} - -
    `}_renderDeviceInbox(){const t=this.host._deviceInbox,e=this._deviceInboxRows(),s=Object.fromEntries(["on_plan","available","hidden","readd"].map(t=>[t,e.filter(e=>e.category===t).length])),i=function(t,e,s,i=!1){const o=s.trim().toLocaleLowerCase();return t.filter(t=>t.category===e&&(!i||t.isNew)&&(!o||t.searchText.includes(o)))}(e,t.tab,t.search,"on_plan"===t.tab&&t.onlyNew),o=i.slice(0,t.limit),a=t=>this.host._t(`device_inbox.tab_${t}`),n=`device_inbox.empty_${t.tab}`;return p`this.host._deviceInbox=null}> -
    -
    - this.host._deviceInbox={...t,search:e.target.value,limit:100}} /> - -
    -
    this._deviceInboxTabKey(t)}> - ${["on_plan","available","hidden","readd"].map(e=>p` - `)} -
    -
    - ${"on_plan"===t.tab?p``:d} - ${"available"===t.tab?p``:d} - - - ${this._help("device_inbox.show_hidden.help")} - -
    - ${"available"===t.tab?this._renderDiscoveryFilters(t):d} -
    - ${o.length?o.map(t=>{const e="on_plan"===t.category?t.canFind?p``:p``:"hidden"===t.category?p``:p``,s="active"===t.status.kind?"":this.host._t(`device_inbox.status_${t.status.kind}`);return p`
    - -
    -
    ${t.name} - ${t.isNew?p`${this.host._t("device_inbox.new")}`:d} -
    -
    - ${[t.model,t.integration,t.spaceName,t.areaName].filter(Boolean).join(" · ")} -
    -
    - ${"excluded_integration"===t.reason&&t.integration?this.host._t("device_inbox.reason_excluded_integration",{integration:t.integration}):this.host._t(`device_inbox.reason_${t.reason}`)} - ${s?p`${s}`:d} -
    - ${t.binding} -
    -
    - ${e} - ${t.canEdit||t.canHide||"available"===t.category||"hidden"===t.category||this.host._bindingHasHaPage(t.binding)?p` -
    - - - -
    - ${t.canEdit&&("on_plan"!==t.category||t.canFind)?p``:d} - ${t.canHide?p``:d} - ${"available"===t.category?p``:d} - ${"hidden"===t.category?p``:d} - ${this.host._bindingHasHaPage(t.binding)?p``:d} -
    -
    `:d} -
    -
    `}):p`
    ${this.host._t(n)}
    `} -
    - ${i.length>o.length?p``:d} -
    -
    - -
    -
    `}_markerValueBadgeFields(t){return ki({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerValueSourceFields(t){return xi({touched:t.valueSourceTouched,originalHas:t.originalHasValueSource,original:t.originalValueSource,source:t.valueSource})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=Cs(t.room),s=ys(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=xs(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=nh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null},r=!!i&&("string"==typeof i.area&&i.area.length>0||null===i.area&&!!i.space&&!!i.room_id);return"virtual"===t.binding||t.roomTouched?(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null):r&&(n.space=i.space,n.area=i.area,n.room_id=i.room_id),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${_(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=Si({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return Mi(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:Ss(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=ws(this.host._planHass,i,o),n=ks(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return Di(Ci(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&Pi(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&Ii(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of Ii(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${Ii(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>Ti(e)&&!i.has(e)&&Ri(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(zi(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=Ai(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),o=!e&&this.host._bindingHasHaPage(t.binding),a=this._markerPreviewDevice(t),n=this._effectiveMarkerTapAction(t,a),r=a?Fs(this.host._serverCfg?.spaces.find(t=>t.id===a.space)):null,l=a?this._markerPreviewDevices(a):this.host._devices,h="toggle"===n&&a?this._toggleIntent(a,l):null,c=this._toggleHintLines(h),u=a?Fi(this.host._planHass,a,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:r?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(a.id),lightDevices:l,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,_=a?ws(this.host._planHass,a,l):[],m=a?ks(this.host._planHass,a,_):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!u?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:u?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:u?.valueBadge?.position||t.valueBadgePosition,v=Ni(f),y=!!f&&!_.some(t=>t.key===v),$=_.find(t=>t.key===v),w=Ni(t.valueSource),k=!(!t.valueSource||w&&_.some(t=>t.key===w)),x=_.find(t=>t.key===w),S=u?.valueSource?.sourceKey||"",M=this._markerAutoHasSpatialSource(t),D=!!a&&Hi(this.host._planHass,{...a}),C=Li(t.lightRole,M,D,t.glowMode),P=!C.sourceExists,I=!C.fromSourceEnabled,T=C.passive,R=C.effectiveMode,z="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||M?T?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),A=a?Ii(a):[],F=a&&Ei(a)||"",N=!!t.lightEntity&&!A.includes(t.lightEntity),H=a?Oi(a):[],L=!!t.toggleEntity&&!H.includes(t.toggleEntity),E="toggle"===n?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,O=E?[...E.targets,...E.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",B=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return p`this._closeMarkerDialog()}> -
    - ${"ha_disabled"===i?.kind?p`
    - - ${this.host._t(`marker.ha_disabled_${i.reason}`)} - ${o?p``:d} -
    `:"unverified"===i?.kind&&t.binding?p`
    - - ${this.host._t("marker.ha_registry_limited")} -
    `:d} - - this.host._markerDialog={...t,name:e.target.value}} /> - - -
    - -
    - - -
    - ${"ha"===t.bindingMode?p` - ${t.bindingOpen?p`
    - this.host._markerDialog={...t,bindingFilter:e.target.value}} /> -
    - ${s.map(e=>p`
    {if(e.value===t.binding)return void(this.host._markerDialog={...t,bindingOpen:!1});const s={...t,binding:e.value,bindingOpen:!1,controls:xs(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value),valueSource:null,valueSourceTouched:!0})}}> - ${e.label}${e.sub} -
    `)} - ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} -
    -
    `:d}`:d} -
    - - - - - ${this._renderVacSection(t)} - - - - ${"toggle"===n&&(H.length>1||L)?p`
    -
    - - ${this._help("marker.toggle_entity.help")} -
    - - ${L?p`

    - - ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:O||this.host._t("marker.toggle_entity_none")})} -

    `:d} -
    `:d} - ${"toggle"===n?p`
    - ${c.map(t=>p`
    ${t}
    `)} -
    -
    ${t.tapHintAnnouncement}
    `:d} - ${"run"===n?(()=>{const e=t.runFilter.trim().toLowerCase(),s=this._runCandidates().filter(t=>!e||t.label.toLowerCase().includes(e)||t.value.includes(e)),i=t.tapTarget?this._runCandidates().find(e=>e.value===t.tapTarget):null;return p` - - ${t.tapTarget&&!i?p`
    ${this.host._t("marker.run_target_gone",{id:t.tapTarget})}
    `:d} - {t.target.select()}} - @input=${e=>this.host._markerDialog={...t,runFilter:e.target.value,tapTarget:""}} /> - ${i?d:p`
    - ${s.slice(0,40).map(e=>p`
    this.host._markerDialog={...t,tapTarget:e.value,runFilter:""}}> - ${e.label}${e.sub} -
    `)} - ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} -
    `}`})():d} - ${"run"===n||"toggle"===n?p``:d} - -
    - - ${this._help("marker.controls.help")} -
    - ${t.controls.length?p`
    - ${t.controls.map(e=>{const s=this._controlRefInfo(e);return p` - ${s.label} - this.host._markerDialog=this._announceToggleDraft({...t,controls:t.controls.filter(t=>t!==e)})}> - `})} -
    `:d} - this.host._markerDialog={...t,controlsFilter:e.target.value}} /> - ${t.controlsFilter.trim()?p`
    - ${this._controlCandidates(t).map(e=>p``)} -
    `:d} - - ${this.host._bindingHasClimate(t.binding)?p``:d} -
    - ${this.host._t("marker.light_role_label")}${this._help("marker.light_role.help")} -
    - - - -
    -
    - - ${"always"===t.lightRole&&(A.length>1||N)?p`
    -
    - - ${this._help("marker.light_entity.help")} -
    - - ${N?p`

    - - ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:F||"—"})} -

    `:d} -
    `:d} - -
    - ${this.host._t("marker.glow_color_label")}${this._help("marker.glow_mode.help")} -
    - - - -
    - ${"auto"!==R?p`
    - {this.host._markerDialog={...t,glowMode:R,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> - ${"fixed"===R?p` - ${this.host._t("marker.glow_brightness")} - ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:R,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},P,this.host._t("marker.glow_brightness"))} - ${Math.round(t.glowBrightness)}%`:d} -
    `:d} -
    -
    -
    - - ${this._help("marker.glow_radius.help")} -
    -
    - this.host._markerDialog={...t,glowRadius:e.target.value}} /> - ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} -
    -
    - ${P||T?p`

    - ${z} -

    `:d} - - - ${customElements.get("ha-icon-picker")?p`{const s=e.detail.value||"";(t.icon||s!==t.autoIcon)&&(this.host._markerDialog={...t,icon:s})}}>`:p`this.host._markerDialog={...t,icon:e.target.value}} />`} - ${!t.icon&&t.autoIcon?p`

    - ${this.host._t("marker.icon_auto",{icon:t.autoIcon})} -

    `:d} - - - -

    ${this.host._t(ih[t.display])}

    - ${"value"===t.display?p`
    -
    - - ${this._help("marker.value_source.help")} -
    - - ${x?p`

    ${x.technical}

    `:d} - ${e?p`

    - - ${this.host._t("marker.preview.reason.value_virtual")} -

    `:k?p`

    - ${this.host._t("marker.value_source_missing_hint")} -

    `:d} -
    `:d} - ${"static_icon"===t.display&&this.host._bindingHasAlarm(t.binding)?p`
    - - ${this.host._t("marker.static_alarm_warning")} -
    `:d} -
    - ${this.host._t("marker.value_badge_title")}${this._help("marker.value_badge.help")} - - ${"static_icon"===t.display?p`

    - ${this.host._t("marker.value_badge_static")} -

    `:_.length||t.valueBadgeSource?d:p`

    - ${this.host._t("marker.value_badge_empty")} -

    `} - ${g?p` -
    - - ${this._help("marker.value_badge_source.help")} -
    - - ${$?p`

    ${$.technical}

    `:d} - ${y?p`

    - ${this.host._t("marker.value_badge_missing_hint")} -

    `:d} - ${"value"===t.display&&v===S?p`

    - ${this.host._t("marker.value_badge_duplicate")} -

    `:d} -
    - - ${this._help("marker.value_badge_position.help")} -
    - - `:d} -
    - ${u?p` - `:p`
    - - ${this.host._t("marker.preview.select_source")} -
    `} - ${"icon_ripple"===t.display?p`
    - {this.host._markerDialog={...t,rippleColor:e.detail.color}}}> -
    -
    - ${this.host._t("marker.ripple_size")} - ${this._rangeInput(1,8,.5,t.rippleSize,e=>this.host._markerDialog={...t,rippleSize:e})} - ×${t.rippleSize} -
    -

    ${this.host._t("marker.activity_alarm_note")}

    `:d} - - -
    - ${this._rangeInput(.5,3,.1,t.size,e=>this.host._markerDialog={...t,size:e})} - ×${t.size.toFixed(1)} - ${this.host._t("marker.angle_label")} - ${""} - ${this._rangeInput(0,355,5,t.angle,e=>this.host._markerDialog={...t,angle:e})} - ${t.angle}° -
    - - - this.host._markerDialog={...t,model:e.target.value}} /> - - - this.host._markerDialog={...t,link:e.target.value}} /> - - - - - -
    - ${t.pdfs.map(t=>p` - ${t.name} - this._removeMarkerPdf(t.url)}>`)} - - - this._pickMarkerFiles(t)} /> - -
    -
    -
    -
    - ${t.devId?p``:d} - ${t.devId?p``:d} -
    -
    - - -
    -
    -
    `}_renderSpaceDialog(){const t=this.host._spaceDialog,e=this.host._importTotal>0&&"create"===t.mode?this.host._t("import.progress",{i:this.host._importTotal-this.host._importQueue.length,n:this.host._importTotal}):"",s=()=>{this.host._spaceDialog=null,this.host._importQueue=[],this.host._importTotal=0};return p` -
    - - this.host._spaceDialog={...t,title:e.target.value}} /> - - - ${"file"===t.source?p`
    - ${t.planFile?p`${t.planFile.name}`:t.planUrl?p`${this.host._t("space.plan_alt")}`:p`${this.host._t("space.no_plan")}`} - - - this._pickPlanFile(t)} /> - - -
    - ${t.pickSaved?this._renderServerPlans(t):d}`:d} - - -
    - - ${this._help("space.cell_cm.help")} -
    -
    - {const s=e.target.value,i=nh(s),o=null==i?null:qi(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(ah,o)):t.cellCm}}} /> - ${this.host._t(this.host._imperial?"space.scale_unit_imperial":"space.scale_unit")} -
    - - - -
    - - ${this._help("space.zero_wall_style.help")} -
    - - - - ${""} - -
    ${this.host._t("space.hide_decor_tip")}
    - -
    ${this.host._t("space.hide_openings_tip")}
    - - ${[["labelTemp","space.label_temp"],["labelHum","space.label_hum"],["labelLqi","space.label_lqi"],["labelLight","space.label_light"]].map(([e,s])=>p``)} - -
    - ${this._rangeInput(50,300,5,Math.round(100*t.cardFontScale),e=>this.host._spaceDialog={...t,cardFontScale:e/100})} - ${Math.round(100*t.cardFontScale)}% -
    - ${this.host._renderCardPreview(t.cardFontScale,1,1)} -
    - {this.host._spaceDialog={...t,roomColor:e.detail.color,roomOpacity:e.detail.opacity}}}> -
    -
    - - ${this._help("space.bg_mode.help")} -
    - - ${"static"===(t.bgMode??Yt(this.host._settings,{}))?p`
    - {this.host._spaceDialog={...t,bgColor:e.detail.color}}}> - ${t.bgColor?p``:p`${this.host._t("space.bg_inherited")}`} -
    `:d} -
    - - ${this._help("space.north.help")} -
    -
    - {const s=e.target.value.trim(),i=""===s?null:Math.round(Number(s));this.host._spaceDialog={...t,northDeg:null!==i&&Number.isFinite(i)?Math.min(359,Math.max(0,i)):null}}} /> - ${null===t.northDeg?this.host._t("space.north_inherited",{v:null===Zt(this.host._settings,{})?"—":String(Zt(this.host._settings,{}))+"°"}):"°"} -
    - - -
    - ${this.host._t("space.fill_label")} - ${this._help("space.fill_mode.help")} -
    - ${Ui.map(t=>[t,"fill."+t]).map(([e,s])=>p` - ${"custom"===e&&"custom"===t.fillMode?p`
    - ${this.host._t("space.custom_fill")} - {this.host._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}> - ${t.customFill?p``:d} -
    `:d}`)} - - ${t.deleteBlockers?p``:d} -
    - -
    `}_renderMergeDialog(){const t=this.host._mergeDialog,e=this.host._spaceModel()?.rooms||[],s=(s,i)=>{const o=e.find(t=>t.id===s),a=o?.area?this.host.hass.areas[o.area]?.name:null;return p``};return p`this.host._mergeDialog=null}> -
    -

    ${this.host._t("merge.hint")}

    - - ${s(t.aId,"a")} - ${s(t.bId,"b")} -
    -
    - - - -
    -
    `}_renderRoomSource(t){const e="temp"===t?this.host._roomTempSrc:this.host._roomHumSrc,s=e=>{"temp"===t?this.host._roomTempSrc=e:this.host._roomHumSrc=e,this.host.requestUpdate()},i=this.host._roomSrcOpen===t;return p` - - - - ${e||i?p` - ${i?p`
    - {this.host._roomSrcFilter=t.target.value,this.host.requestUpdate()}} /> -
    - ${this._roomSrcCandidates().map(t=>p`
    {s(t.value),this.host._roomSrcOpen=null}}> - ${t.label}${t.sub} -
    `)} -
    -
    `:d}`:d}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),o=Fs(this.host._curSpaceCfg),a=this.host._roomFill||o.fill,n=this.host._roomCustomFill||o.customFill,r=[...this.host._freeAreas];if(t&&this.host._areaSel&&!r.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&r.unshift(t)}return p`this._roomDialogCancel()}> -
    - ${s?p`

    - ${s} -

    `:d} - - this.host._nameSel=t.target.value} /> - - - - - - ${[["","fill.inherit"],...Ki.map(t=>[t,"fill."+t])].map(([t,e])=>p``)} - ${"custom"===a?p`
    - ${this.host._roomCustomFill?this.host._t("room.custom_fill_own"):this.host._t("room.custom_fill_space")} - {this.host._roomCustomFill={c:t.detail.color,a:t.detail.opacity}}}> - ${this.host._roomCustomFill?p``:d} -
    `:d} - ${this._renderRoomSource("temp")} - ${this._renderRoomSource("hum")} - - - -
    - ${this._rangeInput(50,300,5,Math.round(100*this.host._roomNameScale),t=>{this.host._roomNameScale=t/100,this.host.requestUpdate()})} - ${Math.round(100*this.host._roomNameScale)}% -
    - -
    - ${this._rangeInput(50,300,5,Math.round(100*this.host._roomLabelScale),t=>{this.host._roomLabelScale=t/100,this.host.requestUpdate()})} - ${Math.round(100*this.host._roomLabelScale)}% -
    - ${this.host._renderCardPreview(Fs(this.host._curSpaceCfg).cardFontScale,this.host._roomNameScale,this.host._roomLabelScale)} -
    -
    - - - ${t?p``:p`${this.host._pendingSplit?d:p``} - `} -
    -
    `}}export{dh as EDITOR_RUNTIME_FINGERPRINT,ph as HouseplanEditorRuntime}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js b/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js similarity index 97% rename from custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js rename to custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js index aa35b134..1bc952c4 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js +++ b/custom_components/houseplan/frontend/houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js @@ -1,5 +1,5 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{l as t,aS as s,A as e,t as o,c as a,cf as l,cg as i,ch as h,ci as c,aP as n,cj as r,co as p,ck as _,cl as d,cm as u,cn as g,df as m,aO as b,aQ as $,dg as v}from"./houseplan-card-DcH_msOh.js";import{i as f,c as y,e as w,r as D,a as k,b as S,d as C,t as M}from"./backdrop-pick-CXzP7hUG.js";const F=1e3,x=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},T="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";class L{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_help(l){const i=`${l}.aria`,h=t(this.host.hass,this.host._config?.language);return s(h,l)&&s(h,i)?a``:e}_openSpaceDialog(t,s){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===t){const e=this.host._serverCfg.spaces.find(t=>t.id===s);if(!e)return;const o=l(e),a=e.settings?.custom_fill&&"object"==typeof e.settings.custom_fill?i(e.settings.custom_fill):null,p="none"===o.fill?{...a||h,a:0}:a;return void(this.host._spaceDialog={mode:t,spaceId:s,title:e.title,planUrl:e.plan_url||null,planFile:null,source:e.plan_url?"file":"draw",showBorders:o.showBorders,showNames:o.showNames,zeroWallStyle:r(e),displayTouched:!0,hideDecor:o.hideDecor,hideOpenings:o.hideOpenings,roomColor:o.color,roomOpacity:o.opacity,fillMode:"none"===o.fill?"custom":o.fill,customFill:p,glowEnabled:o.glow,bgColor:o.bgColor,bgMode:"static"===e.settings?.bg_mode||"daynight"===e.settings?.bg_mode?e.settings.bg_mode:null,northDeg:n({},e.settings),sunRays:"boolean"==typeof e.settings?.sun_rays?e.settings.sun_rays:null,tempMin:o.tempMin,tempMax:o.tempMax,showLqi:o.showLqi??this.host._config?.show_signal??!0,cardFontScale:o.cardFontScale,labelTemp:o.labelTemp,labelHum:o.labelHum,labelLqi:o.labelLqi,labelLight:o.labelLight,cellCm:Number(e.cell_cm)>0?Number(e.cell_cm):5,cellCmInput:c(Number(e.cell_cm)>0?Number(e.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const e=p(this.host._imperial);this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:{...h,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:d,tempMax:_,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:e,cellCmInput:c(e,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await y(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await w(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return D(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??e}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const s=this.host._spaceDialog,e=s?.saved?.find(s=>s.name===t);if(!s||!e||e.used_by.length||e.url===s.planUrl)return;const o=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._spaceDialog,l=a?.saved?.find(s=>s.name===t);if(o&&a&&l&&l.url===e.url&&l.modified===e.modified&&!l.used_by.length&&l.url!==a.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return a`
    ${this.host._t("space.loading")}
    `;const s=t.saved||[];if(!s.length)return a`
    ${this.host._t("space.no_saved")}
    `;return a`
    +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{l as t,a$ as s,A as e,z as o,c as a,cj as l,ck as i,cl as h,cm as c,aY as n,cn as r,cs as p,co as d,cp as _,cq as u,cr as g,dj as m,aX as b,aZ as $,dk as v}from"./houseplan-card-Csq_DR2Y.js";import{i as f,c as y,e as w,r as D,a as k,b as S,d as C,t as M}from"./backdrop-pick-CEZBr4qx.js";const F=1e3,x=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},T="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";class L{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_help(l){const i=`${l}.aria`,h=t(this.host.hass,this.host._config?.language);return s(h,l)&&s(h,i)?a``:e}_openSpaceDialog(t,s){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===t){const e=this.host._serverCfg.spaces.find(t=>t.id===s);if(!e)return;const o=l(e),a=e.settings?.custom_fill&&"object"==typeof e.settings.custom_fill?i(e.settings.custom_fill):null,p="none"===o.fill?{...a||h,a:0}:a;return void(this.host._spaceDialog={mode:t,spaceId:s,title:e.title,planUrl:e.plan_url||null,planFile:null,source:e.plan_url?"file":"draw",showBorders:o.showBorders,showNames:o.showNames,zeroWallStyle:r(e),displayTouched:!0,hideDecor:o.hideDecor,hideOpenings:o.hideOpenings,roomColor:o.color,roomOpacity:o.opacity,fillMode:"none"===o.fill?"custom":o.fill,customFill:p,glowEnabled:o.glow,bgColor:o.bgColor,bgMode:"static"===e.settings?.bg_mode||"daynight"===e.settings?.bg_mode?e.settings.bg_mode:null,northDeg:n({},e.settings),sunRays:"boolean"==typeof e.settings?.sun_rays?e.settings.sun_rays:null,tempMin:o.tempMin,tempMax:o.tempMax,showLqi:o.showLqi??this.host._config?.show_signal??!0,cardFontScale:o.cardFontScale,labelTemp:o.labelTemp,labelHum:o.labelHum,labelLqi:o.labelLqi,labelLight:o.labelLight,cellCm:Number(e.cell_cm)>0?Number(e.cell_cm):5,cellCmInput:c(Number(e.cell_cm)>0?Number(e.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const e=p(this.host._imperial);this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:{...h,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:_,tempMax:d,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:e,cellCmInput:c(e,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await y(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await w(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return D(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??e}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const s=this.host._spaceDialog,e=s?.saved?.find(s=>s.name===t);if(!s||!e||e.used_by.length||e.url===s.planUrl)return;const o=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._spaceDialog,l=a?.saved?.find(s=>s.name===t);if(o&&a&&l&&l.url===e.url&&l.modified===e.modified&&!l.used_by.length&&l.url!==a.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return a`
    ${this.host._t("space.loading")}
    `;const s=t.saved||[];if(!s.length)return a`
    ${this.host._t("space.no_saved")}
    `;return a`
    ${s.map(s=>{return a`
    @@ -16,7 +16,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef
    `;var e})} -
    `}async _saveConfigNow(){this.host._cfgEpoch++;try{await this.host._writeConfig()}catch(t){const s=t;throw s?.physicalGeometryRolledBack?await this.host._reloadRejectedPhysicalWrite():"conflict"===s?.code&&await this.host._reloadConfigOnly(),t}}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const s="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const e="create"===t.mode?`s${Date.now().toString(36)}`:t.spaceId;let o=null;if("file"===t.source&&t.planFile){o={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:e,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let a=t.savedAspect||null;!o&&"file"===t.source&&t.planUrl&&!a&&this.host._aspectJob&&(a=await this.host._aspectJob||null);const l=this.host._serverCfg;let i=l.spaces.find(t=>t.id===e);if("create"===t.mode)i=k(e,t.title.trim()),l.spaces.push(i);else{if(!i)throw new Error(`space ${e} is gone from the config`);i.title=t.title.trim()}if(!i)throw new Error(`space ${e} is unavailable`);if(o?(i.plan_url=o.url,i.plan_aspect=o.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==i.plan_url&&(i.plan_url=t.planUrl,i.plan_aspect=a),"draw"===t.source&&(i.plan_url=null,i.plan_aspect=null,delete i.plan_x,delete i.plan_y,delete i.plan_scale,delete i.plan_scale_x,delete i.plan_scale_y,delete i.plan_angle),i.settings={...i.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):d,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):_,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},i.zero_wall_style=t.zeroWallStyle,i.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(F,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(i.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length)this._openNextImport();else if(s||this.host._importTotal>0){const t=this.host._importTotal>0;this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),await this.host._requestMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._showToast(this.host._t(s&&!t?"toast.space_added_onboard":"import.done"))}else this.host._showToast(this.host._t("create"===t.mode?"toast.space_added":"toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?await this.host._requestMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._saveNav()))}catch(t){const s=t;"conflict"!==s?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const s=this.host._serverCfg;if(!s)return;const e=s.spaces.find(s=>s.id===t.spaceId);if(!e)return;const o=S(s,this.host._layout||{},t.spaceId||""),a=1===s.spaces.length&&s.spaces[0]?.id===t.spaceId;if(o.count&&!a)return void(this.host._spaceDialog={...t,deleteBlockers:o.count});const l=t.spaceId,i=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:this.host._t("confirm.delete_space_body"),objectName:e.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),h=this.host._spaceDialog,c=this.host._serverCfg;if(!i||!h||"edit"!==h.mode||h.busy||h.spaceId!==l||!c)return;if(!c.spaces.find(t=>t.id===l))return;const n=S(c,this.host._layout||{},l),r=1===c.spaces.length&&c.spaces[0]?.id===l;if(!n.count||r){this.host._spaceDialog={...h,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:l,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[s,e]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(s,e),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===l&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){const s=t;"conflict"!==s?.code&&"space_in_use"!==s?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=S(e,this.host._layout||{},l),s=1===e.spaces.length&&e.spaces[0]?.id===l;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...h,deleteBlockers:n.count}}_startImport(){const t=this.host._importDialog;if(!t)return;const s=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,s.length?(this.host._importQueue=s,this.host._importTotal=s.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();if(void 0===t)return;const s=p(this.host._imperial);this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:d,tempMax:_,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:s,cellCmInput:c(s,this.host._imperial),cellCmTouched:!1,busy:!1}}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this.host._requestMode("plan").then(()=>{this.host._showToast(this.host._t("import.done"))}))}_renderImportDialog(){const t=this.host._importDialog,s=t.floors.filter(t=>t.checked).length;return a``}async _saveConfigNow(){this.host._cfgEpoch++;try{await this.host._writeConfig()}catch(t){const s=t;throw s?.physicalGeometryRolledBack?await this.host._reloadRejectedPhysicalWrite():"conflict"===s?.code&&await this.host._reloadConfigOnly(),t}}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const s="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const e="create"===t.mode?`s${Date.now().toString(36)}`:t.spaceId;let o=null;if("file"===t.source&&t.planFile){o={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:e,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let a=t.savedAspect||null;!o&&"file"===t.source&&t.planUrl&&!a&&this.host._aspectJob&&(a=await this.host._aspectJob||null);const l=this.host._serverCfg;let i=l.spaces.find(t=>t.id===e);if("create"===t.mode)i=k(e,t.title.trim()),l.spaces.push(i);else{if(!i)throw new Error(`space ${e} is gone from the config`);i.title=t.title.trim()}if(!i)throw new Error(`space ${e} is unavailable`);if(o?(i.plan_url=o.url,i.plan_aspect=o.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==i.plan_url&&(i.plan_url=t.planUrl,i.plan_aspect=a),"draw"===t.source&&(i.plan_url=null,i.plan_aspect=null,delete i.plan_x,delete i.plan_y,delete i.plan_scale,delete i.plan_scale_x,delete i.plan_scale_y,delete i.plan_angle),i.settings={...i.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):_,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):d,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},i.zero_wall_style=t.zeroWallStyle,i.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(F,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(i.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length)this._openNextImport();else if(s||this.host._importTotal>0){const t=this.host._importTotal>0;this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),await this.host._requestMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._showToast(this.host._t(s&&!t?"toast.space_added_onboard":"import.done"))}else this.host._showToast(this.host._t("create"===t.mode?"toast.space_added":"toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?await this.host._requestMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._saveNav()))}catch(t){const s=t;"conflict"!==s?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const s=this.host._serverCfg;if(!s)return;const e=s.spaces.find(s=>s.id===t.spaceId);if(!e)return;const o=S(s,this.host._layout||{},t.spaceId||""),a=1===s.spaces.length&&s.spaces[0]?.id===t.spaceId;if(o.count&&!a)return void(this.host._spaceDialog={...t,deleteBlockers:o.count});const l=t.spaceId,i=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:this.host._t("confirm.delete_space_body"),objectName:e.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),h=this.host._spaceDialog,c=this.host._serverCfg;if(!i||!h||"edit"!==h.mode||h.busy||h.spaceId!==l||!c)return;if(!c.spaces.find(t=>t.id===l))return;const n=S(c,this.host._layout||{},l),r=1===c.spaces.length&&c.spaces[0]?.id===l;if(!n.count||r){this.host._spaceDialog={...h,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:l,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[s,e]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(s,e),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===l&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){const s=t;"conflict"!==s?.code&&"space_in_use"!==s?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=S(e,this.host._layout||{},l),s=1===e.spaces.length&&e.spaces[0]?.id===l;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...h,deleteBlockers:n.count}}_startImport(){const t=this.host._importDialog;if(!t)return;const s=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,s.length?(this.host._importQueue=s,this.host._importTotal=s.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();if(void 0===t)return;const s=p(this.host._imperial);this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:_,tempMax:d,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:s,cellCmInput:c(s,this.host._imperial),cellCmTouched:!1,busy:!1}}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this.host._requestMode("plan").then(()=>{this.host._showToast(this.host._t("import.done"))}))}_renderImportDialog(){const t=this.host._importDialog,s=t.floors.filter(t=>t.checked).length;return a`this.host._importDialog=null}>
    ${this.host._t("import.hint")}
    diff --git a/custom_components/houseplan/frontend/houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js b/custom_components/houseplan/frontend/houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js deleted file mode 100644 index 41064d03..00000000 --- a/custom_components/houseplan/frontend/houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js +++ /dev/null @@ -1,36 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{k as e,A as t,du as i,aw as s,c as o,l as r,j as n}from"./houseplan-card-DcH_msOh.js";import{a,r as h,t as d}from"./zigbee-topology-x104QxRs.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-BFjOXm55.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o` - ${l.length?s``:t} - ${l.map(e=>o`
    `)} - ${p.remoteCount?o`
    ${d(r(this.hass),"remote_count",{n:p.remoteCount})}
    `:t} - `}}v.properties={hass:{attribute:!1},devices:{attribute:!1},registry:{attribute:!1},currentSpace:{type:String,attribute:"current-space"},viewKey:{attribute:!1}},v.styles=n` - :host { position: absolute; inset: 0; z-index: 5; display: block; pointer-events: none; } - svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; } - line { vector-effect: non-scaling-stroke; stroke-linecap: round; } - .halo { - position: absolute; width: calc(var(--device-base-size, 4cqw) * 1.22); - height: calc(var(--device-base-size, 4cqw) * 1.22); transform: translate(-50%, -50%); - box-sizing: border-box; border: 2px solid rgba(120, 190, 220, .82); border-radius: 50%; - box-shadow: 0 0 0 4px rgba(120, 190, 220, .16); - } - .remote { - position: absolute; transform: translate(12px, calc(-100% - 12px)); - border: 1px solid rgba(255,255,255,.7); border-radius: 999px; - padding: 3px 7px; color: #fff; background: rgba(28,31,36,.88); - box-shadow: 0 2px 7px rgba(0,0,0,.28); white-space: nowrap; - font: 600 11px/1.2 system-ui, sans-serif; - } - @media (forced-colors: active) { - line { stroke: Highlight !important; } - .halo { border-color: Highlight; box-shadow: none; } - .remote { color: CanvasText; background: Canvas; border-color: CanvasText; } - } - `,customElements.get("hp-zigbee-topology-overlay")||customElements.define("hp-zigbee-topology-overlay",v);export{v as HpZigbeeTopologyOverlay}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js b/custom_components/houseplan/frontend/houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js new file mode 100644 index 00000000..5f5c79d7 --- /dev/null +++ b/custom_components/houseplan/frontend/houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js @@ -0,0 +1,36 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{v as e,A as t,dx as i,w as s,c as o,l as r,u as n}from"./houseplan-card-Csq_DR2Y.js";import{a,r as h,t as d}from"./zigbee-topology-D9L1ESh4.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-DUxfg4Z9.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o` + ${l.length?s``:t} + ${l.map(e=>o`
    `)} + ${p.remoteCount?o`
    ${d(r(this.hass),"remote_count",{n:p.remoteCount})}
    `:t} + `}}v.properties={hass:{attribute:!1},devices:{attribute:!1},registry:{attribute:!1},currentSpace:{type:String,attribute:"current-space"},viewKey:{attribute:!1}},v.styles=n` + :host { position: absolute; inset: 0; z-index: 5; display: block; pointer-events: none; } + svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; } + line { vector-effect: non-scaling-stroke; stroke-linecap: round; } + .halo { + position: absolute; width: calc(var(--device-base-size, 4cqw) * 1.22); + height: calc(var(--device-base-size, 4cqw) * 1.22); transform: translate(-50%, -50%); + box-sizing: border-box; border: 2px solid rgba(120, 190, 220, .82); border-radius: 50%; + box-shadow: 0 0 0 4px rgba(120, 190, 220, .16); + } + .remote { + position: absolute; transform: translate(12px, calc(-100% - 12px)); + border: 1px solid rgba(255,255,255,.7); border-radius: 999px; + padding: 3px 7px; color: #fff; background: rgba(28,31,36,.88); + box-shadow: 0 2px 7px rgba(0,0,0,.28); white-space: nowrap; + font: 600 11px/1.2 system-ui, sans-serif; + } + @media (forced-colors: active) { + line { stroke: Highlight !important; } + .halo { border-color: Highlight; box-shadow: none; } + .remote { color: CanvasText; background: Canvas; border-color: CanvasText; } + } + `,customElements.get("hp-zigbee-topology-overlay")||customElements.define("hp-zigbee-topology-overlay",v);export{v as HpZigbeeTopologyOverlay}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/live-interaction-runtime-C2JvTP_O.js b/custom_components/houseplan/frontend/houseplan-assets/live-interaction-runtime-C2JvTP_O.js new file mode 100644 index 00000000..19cb04f2 --- /dev/null +++ b/custom_components/houseplan/frontend/houseplan-assets/live-interaction-runtime-C2JvTP_O.js @@ -0,0 +1 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{dx as e}from"./houseplan-card-Csq_DR2Y.js";const t=new WeakMap,n=e=>{let n=t.get(e);return n||(n={painted:null,pending:null,raf:0},t.set(e,n)),n},o=e=>`${e.x} ${e.y} ${e.w} ${e.h}`;const r=e=>0===e.translateXPercent&&0===e.translateYPercent&&1===e.scaleX&&1===e.scaleY,a=e=>[e.x,e.y,e.w,e.h].every(Number.isFinite)&&e.w>0&&e.h>0,s=(e,t)=>{if(!t)return e.style.removeProperty("transform"),e.style.removeProperty("transform-origin"),void e.style.removeProperty("will-change");e.style.transformOrigin="0 0",e.style.willChange="transform",e.style.transform=`translate(${t.translateXPercent}%,${t.translateYPercent}%) scale(${t.scaleX},${t.scaleY})`};function i(e,t,n){if(!a(t.view)||!a(n.view)||!a(n.floor))return;const i=o(n.view),l=o(n.floor);for(const t of e.querySelectorAll('[data-hp-live-viewbox="camera"]'))t.setAttribute("viewBox",i);for(const t of e.querySelectorAll('[data-hp-live-viewbox="floor"]'))t.setAttribute("viewBox",l);const c=(f=t.view,u=n.view,{translateXPercent:(f.x-u.x)/u.w*100,translateYPercent:(f.y-u.y)/u.h*100,scaleX:f.w/u.w,scaleY:f.h/u.h});var f,u;for(const t of e.querySelectorAll('[data-hp-live-layer="camera"]'))s(t,r(c)?null:c);const d=e.querySelector("[data-hp-live-zoom]");if(d){d.hidden=n.zoom<=1;const e=d.querySelector("[data-hp-live-zoom-value]");e&&(e.textContent=`${Math.round(100*n.zoom)}%`)}}const l=e=>{const t=e._viewOr(e._baseVb());return{view:{...t},floor:{...e._floorView(t)},zoom:e._zoom}};const c=new WeakMap,f=(e,t,n,o)=>{if(!n)return;const r=document.createElement("span");if(r.className="m",t){r.append(`${t} `);const e=document.createElement("b");e.textContent=n,o&&(e.style.color=o),r.append(e)}else r.textContent=n;e.append(r)},u=(e,t,n)=>{const o=e.querySelector(t);o&&(o.setAttribute("d",n),o.toggleAttribute("hidden",!n))};function d(t){const n=t,o=n.renderRoot;if(!o)return;((t,n)=>{const o=n.querySelector("[data-hp-live-tip]");if(!o)return;const r=t._tip;if(!r)return o.hidden=!0,void o.replaceChildren();o.style.left=`${r.x+12}px`,o.style.top=`${r.y+12}px`,o.replaceChildren();const a=document.createElement("b");a.textContent=r.title,o.append(a),f(o,"",r.meta),f(o,t._t("tip.temp_avg"),null==r.temp?"":`${r.temp}°`),f(o,t._t("tip.hum_avg"),null==r.hum?"":`${r.hum}%`),f(o,t._t("tip.lqi"),null==r.lqi?"":String(r.lqi),null==r.lqi?void 0:e(r.lqi)),o.hidden=!1})(n,o);const r=n._hoverRoom,a=c.get(t),s=r?.space||"",i=r?.room||null;if(a?.room===i&&a?.space===s)return;c.set(t,{room:i,space:s});const l=r&&n._spaceModel(),d=l?n._roomHoverPaths(l):null;u(o,'[data-hp-live-room-hover="fill"]',d?.fillD||""),u(o,'[data-hp-live-room-hover="halo"]',d?.outlineD||""),u(o,'[data-hp-live-room-hover="outline"]',d?.outlineD||"")}function m(e){c.delete(e)}class p{constructor(e){this.host=e,this.deferredHass=!1}hass(e,t,n,o){const r=function(e,t,n){if(e===t)return"none";if(!e||!t||!n)return"structural";if(e.connection!==t.connection||e.entities!==t.entities||e.devices!==t.devices||e.areas!==t.areas||e.themes!==t.themes||e.user!==t.user||e.config!==t.config||e.floors!==t.floors||e.services!==t.services||e.panels!==t.panels||e.language!==t.language||e.locale?.language!==t.locale?.language||e.locale?.number_format!==t.locale?.number_format||e.locale?.time_format!==t.locale?.time_format||e.locale?.date_format!==t.locale?.date_format)return"structural";const o=new Set(["states","connection","entities","devices","areas","themes","user","config","floors","services","panels","language","locale"]);for(const n of new Set([...Object.keys(e),...Object.keys(t)]))if(!o.has(n)&&e[n]!==t[n])return"structural";for(const o of n.entityIds)if(e.states?.[o]!==t.states?.[o])return"state";return"none"}(e,t,n),a="state"===r&&this.active();a&&(this.deferredHass=!0);const s="none"!==r&&!a;return s||this.host._renderLife.observe(e,t,n,o),s}clear(){this.deferredHass=!1}take(){const e=this.deferredHass;return this.deferredHass=!1,e}viewport(e=!1){!function(e,t=!1){const o=e,r=n(e),a=l(o);if(t)return r.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(r.raf),r.raf=0,r.pending=null,r.painted||(r.painted=a),void i(o.renderRoot,r.painted,a);r.pending=a,r.raf||"function"!=typeof requestAnimationFrame||(r.raf=requestAnimationFrame(()=>{r.raf=0;const e=r.pending;r.pending=null;const t=o.renderRoot;e&&t&&(r.painted||(r.painted=e),i(t,r.painted,e))}))}(this.host,e)}hover(){d(this.host)}active(){const e=this.host;return e._pointers.size>0||e._cameraTransition.active||!!e._deviceDrag||!!e._physicalDrag||!!e._physicalRotate||!!e._decorMove||!!e._decorDraft||!!e._dtDrag||!!e._bdDrag||!!e._opDrag||!!e._resize?.dragging}commit(){!function(e){const t=e,o=n(e);o.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(o.raf),o.raf=0,o.pending=null,o.painted=l(t);const r=t.renderRoot;if(r){for(const e of r.querySelectorAll('[data-hp-live-layer="camera"]'))s(e,null);i(r,o.painted,o.painted)}}(this.host),m(this.host),d(this.host)}dispose(){!function(e){const n=t.get(e);n?.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(n.raf),t.delete(e)}(this.host),m(this.host)}}export{p as LiveRuntime}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-x104QxRs.js b/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-D9L1ESh4.js similarity index 98% rename from custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-x104QxRs.js rename to custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-D9L1ESh4.js index 2d346446..237c9c51 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-x104QxRs.js +++ b/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-D9L1ESh4.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{s as e,y as n}from"./houseplan-card-DcH_msOh.js";var t={title:"Zigbee links",toggle:"Show Zigbee links when hovering over a device",hint:"Shows observed direct neighbours only. Nothing is fetched while you hover.",admin_only:"Topology data is available to Home Assistant administrators only.",save_first:"Save this setting before loading topology data.",zha:"ZHA",zha_read:"Read ZHA data",zha_hint:"Reads ZHA's cached topology; it does not start a radio scan.",z2m:"Zigbee2MQTT",z2m_topics:"Base topics (one per line)",z2m_update:"Update map",z2m_warning:"A network-map scan can take 10 seconds to 2 minutes and may temporarily reduce Zigbee responsiveness.",status_idle:"Data not loaded",status_loading:"Loading…",status_ready:"Received {time}",status_stale:"Received {time} · stale",status_partial:"Received {time} · some nodes were omitted",status_no_links:"Received {time} · no observed links found",error_permission:"Administrator permission is required.",error_unsupported:"This provider or Home Assistant API is unavailable.",error_timeout:"The provider did not respond in time.",error_invalid_topic:"Check the Zigbee2MQTT base topic.",error_invalid_payload:"The provider returned unsupported topology data.",error_provider:"Topology data could not be loaded.",remote_count:"+{n} in other spaces"};const i={en:t,ru:{title:"Связи Zigbee",toggle:"Показывать связи Zigbee при наведении на устройство",hint:"Показываются только наблюдаемые прямые соседи. Наведение ничего не запрашивает.",admin_only:"Данные топологии доступны только администраторам Home Assistant.",save_first:"Сохраните настройку перед загрузкой данных топологии.",zha:"ZHA",zha_read:"Прочитать данные ZHA",zha_hint:"Читает сохранённую топологию ZHA и не запускает радио-сканирование.",z2m:"Zigbee2MQTT",z2m_topics:"Базовые топики (по одному в строке)",z2m_update:"Обновить карту",z2m_warning:"Сканирование карты сети занимает от 10 секунд до 2 минут и временно может снизить отзывчивость Zigbee.",status_idle:"Данные не загружены",status_loading:"Загрузка…",status_ready:"Получено: {time}",status_stale:"Получено: {time} · данные устарели",status_partial:"Получено: {time} · часть узлов не показана",status_no_links:"Получено: {time} · наблюдаемые связи не найдены",error_permission:"Нужны права администратора.",error_unsupported:"Провайдер или API Home Assistant недоступен.",error_timeout:"Провайдер не ответил вовремя.",error_invalid_topic:"Проверьте базовый топик Zigbee2MQTT.",error_invalid_payload:"Провайдер вернул неподдерживаемые данные топологии.",error_provider:"Не удалось загрузить данные топологии.",remote_count:"+{n} в других пространствах"},de:{title:"Zigbee-Verbindungen",toggle:"Zigbee-Verbindungen beim Darüberfahren über ein Gerät anzeigen",hint:"Es werden nur beobachtete direkte Nachbarn angezeigt. Beim Darüberfahren werden keine Daten abgerufen.",admin_only:"Topologiedaten sind nur für Home-Assistant-Administratoren verfügbar.",save_first:"Speichere diese Einstellung, bevor Topologiedaten geladen werden.",zha:"ZHA",zha_read:"ZHA-Daten lesen",zha_hint:"Liest den ZHA-Topologiecache; es wird kein Funkscan gestartet.",z2m:"Zigbee2MQTT",z2m_topics:"Basistopics (eines pro Zeile)",z2m_update:"Karte aktualisieren",z2m_warning:"Ein Netzwerkscan kann 10 Sekunden bis 2 Minuten dauern und Zigbee vorübergehend verlangsamen.",status_idle:"Daten nicht geladen",status_loading:"Wird geladen…",status_ready:"Empfangen: {time}",status_stale:"Empfangen: {time} · veraltet",status_partial:"Empfangen: {time} · einige Knoten fehlen",status_no_links:"Empfangen: {time} · keine beobachteten Verbindungen gefunden",error_permission:"Administratorrechte sind erforderlich.",error_unsupported:"Dieser Anbieter oder die Home-Assistant-API ist nicht verfügbar.",error_timeout:"Der Anbieter hat nicht rechtzeitig geantwortet.",error_invalid_topic:"Prüfe das Zigbee2MQTT-Basistopic.",error_invalid_payload:"Der Anbieter hat nicht unterstützte Topologiedaten geliefert.",error_provider:"Topologiedaten konnten nicht geladen werden.",remote_count:"+{n} in anderen Bereichen"},fr:{title:"Liens Zigbee",toggle:"Afficher les liens Zigbee au survol d’un appareil",hint:"Seuls les voisins directs observés sont affichés. Le survol ne lance aucune requête.",admin_only:"Les données de topologie sont réservées aux administrateurs Home Assistant.",save_first:"Enregistrez ce réglage avant de charger les données de topologie.",zha:"ZHA",zha_read:"Lire les données ZHA",zha_hint:"Lit la topologie en cache de ZHA sans lancer d’analyse radio.",z2m:"Zigbee2MQTT",z2m_topics:"Topics de base (un par ligne)",z2m_update:"Actualiser la carte",z2m_warning:"L’analyse de la carte réseau peut durer de 10 secondes à 2 minutes et réduire temporairement la réactivité de Zigbee.",status_idle:"Données non chargées",status_loading:"Chargement…",status_ready:"Reçu : {time}",status_stale:"Reçu : {time} · obsolète",status_partial:"Reçu : {time} · certains nœuds sont omis",status_no_links:"Reçu : {time} · aucun lien observé trouvé",error_permission:"Les droits administrateur sont requis.",error_unsupported:"Ce fournisseur ou l’API Home Assistant n’est pas disponible.",error_timeout:"Le fournisseur n’a pas répondu à temps.",error_invalid_topic:"Vérifiez le topic de base Zigbee2MQTT.",error_invalid_payload:"Le fournisseur a renvoyé des données de topologie non prises en charge.",error_provider:"Impossible de charger les données de topologie.",remote_count:"+{n} dans d’autres espaces"}};function r(n,r,o){return e(i[n]?.[r]??t[r]??r,o)}const o=3e5,s=2097152,a=6e3;function d(e){return null!==e&&"object"==typeof e?e:null}function l(e){if("string"!=typeof e&&"number"!=typeof e)return null;let n=String(e).trim().toLowerCase();return n=/^0x[0-9a-f]{16}$/.test(n)?n.slice(2):n.replace(/[:-]/g,""),/^[0-9a-f]{16}$/.test(n)?n:null}function u(e,n,t){return`${e}:${n}:${t}`}function c(e){const n=String(e||"").toLowerCase().replace(/[ _-]/g,"");return n.includes("coordinator")?"coordinator":n.includes("router")?"router":n.includes("enddevice")||"end"===n?"end":"unknown"}function p(e){const n=d(e),t=function(e){const n=Number(e);return Number.isFinite(n)&&n>=0&&n<=255?Math.round(n):void 0}(n?.lqi??n?.linkquality??n?.link_quality),i="string"==typeof n?.relationship?n.relationship.slice(0,40):void 0;return{...void 0===t?{}:{lqi:t},...i?{relationship:i}:{}}}function g(e,n){return Array.isArray(e)&&e.length<=n?e:null}function f(e){if("string"==typeof e)return e.length<=s;try{return JSON.stringify(e).length<=s}catch{return!1}}function _(e,n,t,i,r){if(t===i)return void n.push({code:"self_link",nodeKey:t});const o=t=a){t.push({code:"invalid_payload"});break}const n=d(e),i=l(n?.ieee??n?.ieee_address);if(!i){t.push({code:"invalid_payload",nodeKey:s});continue}const p=u("zha","zha",i);r.has(p)||r.set(p,{key:p,ieee:i,role:c(n?.device_type)}),_(o,t,s,p,e)}else null!=n?.neighbors&&t.push({code:"invalid_payload",nodeKey:s})}return{provider:"zha",instanceId:"zha",obtainedAt:n,freshness:"provider-cache",nodes:[...r.values()],links:[...o.values()],warnings:t}}function m(e,n){const t=d(e),i=l(t?.ieeeAddr??t?.ieee_address??t?.ieee??e);if(i)return i;for(const i of[t?.id,t?.networkAddress,t?.network_address,e])if(null!=i){const e=n.get(String(i));if(e)return e}return null}function v(e,t,i=Date.now()){const r=[],o=n(t)||"invalid",p=f(e)?function(e){let n=e;if("string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}const t=d(n),i=d(t?.data);if(n=i?.value??t?.value??t?.data??n,"string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}return n}(e):null,h=d(p),v=g(h?.nodes,1e3),b=g(h?.links,a);if(!v||!b)return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[],links:[],warnings:[{code:"invalid_payload"}]};const y=new Map,k=new Map;for(const e of v){const n=d(e),t=l(n?.ieeeAddr??n?.ieee_address??n?.ieee);if(!t){r.push({code:"invalid_payload"});continue}const i=u("z2m",o,t);y.set(i,{key:i,ieee:t,role:c(n?.type??n?.device_type),..."boolean"==typeof n?.failed?{available:!n.failed}:{}});for(const e of[n?.id,n?.networkAddress,n?.network_address])null!=e&&k.set(String(e),t)}const z=new Map;for(const e of b){const n=d(e),t=l(n?.sourceIeeeAddr)??m(n?.source,k),i=l(n?.targetIeeeAddr)??m(n?.target,k);if(!t||!i){r.push({code:"invalid_payload"});continue}const s=u("z2m",o,t),a=u("z2m",o,i);y.has(s)||y.set(s,{key:s,ieee:t,role:"unknown"}),y.has(a)||y.set(a,{key:a,ieee:i,role:"unknown"}),_(z,r,s,a,e)}return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[...y.values()],links:[...z.values()],warnings:r}}function b(e){if("string"!=typeof e)return null;const n=l(e);if(n)return n;const t=e.toLowerCase().match(/^zigbee2mqtt(?:_bridge)?_(.+)$/);return t?l(t[1]):null}function y(e,n){if(e.deviceId&&n.devices[e.deviceId])return[e.deviceId];const t=new Set;for(const[i,r]of Object.entries(n.devices)){const n=d(r);(Array.isArray(n?.identifiers)?n.identifiers:[]).some(n=>Array.isArray(n)&&b(n[1])===e.ieee)&&t.add(i)}if(t.size)return[...t];for(const i of Object.values(n.entities)){const n=d(i);"string"==typeof n?.device_id&&"string"==typeof n.unique_id&&(b(n.unique_id)===e.ieee&&t.add(n.device_id))}return[...t]}function k(e){return!e.hidden&&!e.virtual&&"ha_disabled"!==e.bindingStatus?.kind&&"orphaned"!==e.bindingStatus?.kind&&"unverified"!==e.bindingStatus?.kind}function z(e,n,t){const i=new Map,r=[];for(const o of e.nodes){if(!1===o.available){r.push({code:"provider_scan_failure",nodeKey:o.key});continue}const e=y(o,t);if(1!==e.length){r.push({code:"unmatched_device",nodeKey:o.key});continue}const s=e[0];let a=n.filter(e=>k(e)&&"device"===e.bindingKind&&e.bindingRef===s);a.length||(a=n.filter(e=>k(e)&&"entity"===e.bindingKind&&!!e.bindingRef&&t.entities[e.bindingRef]?.device_id===s)),1===a.length?i.set(o.key,{markerId:a[0].id,space:a[0].space}):r.push({code:a.length>1?"ambiguous_placement":"unmatched_device",nodeKey:o.key})}return{placements:i,warnings:r}}function A(e,n,t){return e.map(e=>({topology:e,placements:z(e,n,t).placements}))}function w(e,n,t){const i=new Map,r=new Set;let o=0;for(const{topology:s,placements:a}of e){const e=new Set([...a].filter(([,e])=>e.markerId===t&&e.space===n).map(([e])=>e));if(e.size)for(const d of s.links){const s=e.has(d.a),l=e.has(d.b);if(!s&&!l)continue;const u=a.get(s?d.b:d.a);if(!u||u.markerId===t){o++;continue}if(u.space!==n){r.add(u.markerId);continue}const c=s?d.aToB:d.bToA,p=i.get(u.markerId);(!p||void 0===p.lqi&&void 0!==c?.lqi)&&i.set(u.markerId,{neighborMarkerId:u.markerId,lqi:c?.lqi})}}return{lines:[...i.values()],remoteCount:r.size,omittedCount:o}}export{o as T,A as a,v as b,z as m,h as n,w as r,r as t}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{s as e,I as n}from"./houseplan-card-Csq_DR2Y.js";var t={title:"Zigbee links",toggle:"Show Zigbee links when hovering over a device",hint:"Shows observed direct neighbours only. Nothing is fetched while you hover.",admin_only:"Topology data is available to Home Assistant administrators only.",save_first:"Save this setting before loading topology data.",zha:"ZHA",zha_read:"Read ZHA data",zha_hint:"Reads ZHA's cached topology; it does not start a radio scan.",z2m:"Zigbee2MQTT",z2m_topics:"Base topics (one per line)",z2m_update:"Update map",z2m_warning:"A network-map scan can take 10 seconds to 2 minutes and may temporarily reduce Zigbee responsiveness.",status_idle:"Data not loaded",status_loading:"Loading…",status_ready:"Received {time}",status_stale:"Received {time} · stale",status_partial:"Received {time} · some nodes were omitted",status_no_links:"Received {time} · no observed links found",error_permission:"Administrator permission is required.",error_unsupported:"This provider or Home Assistant API is unavailable.",error_timeout:"The provider did not respond in time.",error_invalid_topic:"Check the Zigbee2MQTT base topic.",error_invalid_payload:"The provider returned unsupported topology data.",error_provider:"Topology data could not be loaded.",remote_count:"+{n} in other spaces"};const i={en:t,ru:{title:"Связи Zigbee",toggle:"Показывать связи Zigbee при наведении на устройство",hint:"Показываются только наблюдаемые прямые соседи. Наведение ничего не запрашивает.",admin_only:"Данные топологии доступны только администраторам Home Assistant.",save_first:"Сохраните настройку перед загрузкой данных топологии.",zha:"ZHA",zha_read:"Прочитать данные ZHA",zha_hint:"Читает сохранённую топологию ZHA и не запускает радио-сканирование.",z2m:"Zigbee2MQTT",z2m_topics:"Базовые топики (по одному в строке)",z2m_update:"Обновить карту",z2m_warning:"Сканирование карты сети занимает от 10 секунд до 2 минут и временно может снизить отзывчивость Zigbee.",status_idle:"Данные не загружены",status_loading:"Загрузка…",status_ready:"Получено: {time}",status_stale:"Получено: {time} · данные устарели",status_partial:"Получено: {time} · часть узлов не показана",status_no_links:"Получено: {time} · наблюдаемые связи не найдены",error_permission:"Нужны права администратора.",error_unsupported:"Провайдер или API Home Assistant недоступен.",error_timeout:"Провайдер не ответил вовремя.",error_invalid_topic:"Проверьте базовый топик Zigbee2MQTT.",error_invalid_payload:"Провайдер вернул неподдерживаемые данные топологии.",error_provider:"Не удалось загрузить данные топологии.",remote_count:"+{n} в других пространствах"},de:{title:"Zigbee-Verbindungen",toggle:"Zigbee-Verbindungen beim Darüberfahren über ein Gerät anzeigen",hint:"Es werden nur beobachtete direkte Nachbarn angezeigt. Beim Darüberfahren werden keine Daten abgerufen.",admin_only:"Topologiedaten sind nur für Home-Assistant-Administratoren verfügbar.",save_first:"Speichere diese Einstellung, bevor Topologiedaten geladen werden.",zha:"ZHA",zha_read:"ZHA-Daten lesen",zha_hint:"Liest den ZHA-Topologiecache; es wird kein Funkscan gestartet.",z2m:"Zigbee2MQTT",z2m_topics:"Basistopics (eines pro Zeile)",z2m_update:"Karte aktualisieren",z2m_warning:"Ein Netzwerkscan kann 10 Sekunden bis 2 Minuten dauern und Zigbee vorübergehend verlangsamen.",status_idle:"Daten nicht geladen",status_loading:"Wird geladen…",status_ready:"Empfangen: {time}",status_stale:"Empfangen: {time} · veraltet",status_partial:"Empfangen: {time} · einige Knoten fehlen",status_no_links:"Empfangen: {time} · keine beobachteten Verbindungen gefunden",error_permission:"Administratorrechte sind erforderlich.",error_unsupported:"Dieser Anbieter oder die Home-Assistant-API ist nicht verfügbar.",error_timeout:"Der Anbieter hat nicht rechtzeitig geantwortet.",error_invalid_topic:"Prüfe das Zigbee2MQTT-Basistopic.",error_invalid_payload:"Der Anbieter hat nicht unterstützte Topologiedaten geliefert.",error_provider:"Topologiedaten konnten nicht geladen werden.",remote_count:"+{n} in anderen Bereichen"},fr:{title:"Liens Zigbee",toggle:"Afficher les liens Zigbee au survol d’un appareil",hint:"Seuls les voisins directs observés sont affichés. Le survol ne lance aucune requête.",admin_only:"Les données de topologie sont réservées aux administrateurs Home Assistant.",save_first:"Enregistrez ce réglage avant de charger les données de topologie.",zha:"ZHA",zha_read:"Lire les données ZHA",zha_hint:"Lit la topologie en cache de ZHA sans lancer d’analyse radio.",z2m:"Zigbee2MQTT",z2m_topics:"Topics de base (un par ligne)",z2m_update:"Actualiser la carte",z2m_warning:"L’analyse de la carte réseau peut durer de 10 secondes à 2 minutes et réduire temporairement la réactivité de Zigbee.",status_idle:"Données non chargées",status_loading:"Chargement…",status_ready:"Reçu : {time}",status_stale:"Reçu : {time} · obsolète",status_partial:"Reçu : {time} · certains nœuds sont omis",status_no_links:"Reçu : {time} · aucun lien observé trouvé",error_permission:"Les droits administrateur sont requis.",error_unsupported:"Ce fournisseur ou l’API Home Assistant n’est pas disponible.",error_timeout:"Le fournisseur n’a pas répondu à temps.",error_invalid_topic:"Vérifiez le topic de base Zigbee2MQTT.",error_invalid_payload:"Le fournisseur a renvoyé des données de topologie non prises en charge.",error_provider:"Impossible de charger les données de topologie.",remote_count:"+{n} dans d’autres espaces"}};function r(n,r,o){return e(i[n]?.[r]??t[r]??r,o)}const o=3e5,s=2097152,a=6e3;function d(e){return null!==e&&"object"==typeof e?e:null}function l(e){if("string"!=typeof e&&"number"!=typeof e)return null;let n=String(e).trim().toLowerCase();return n=/^0x[0-9a-f]{16}$/.test(n)?n.slice(2):n.replace(/[:-]/g,""),/^[0-9a-f]{16}$/.test(n)?n:null}function u(e,n,t){return`${e}:${n}:${t}`}function c(e){const n=String(e||"").toLowerCase().replace(/[ _-]/g,"");return n.includes("coordinator")?"coordinator":n.includes("router")?"router":n.includes("enddevice")||"end"===n?"end":"unknown"}function p(e){const n=d(e),t=function(e){const n=Number(e);return Number.isFinite(n)&&n>=0&&n<=255?Math.round(n):void 0}(n?.lqi??n?.linkquality??n?.link_quality),i="string"==typeof n?.relationship?n.relationship.slice(0,40):void 0;return{...void 0===t?{}:{lqi:t},...i?{relationship:i}:{}}}function g(e,n){return Array.isArray(e)&&e.length<=n?e:null}function f(e){if("string"==typeof e)return e.length<=s;try{return JSON.stringify(e).length<=s}catch{return!1}}function _(e,n,t,i,r){if(t===i)return void n.push({code:"self_link",nodeKey:t});const o=t=a){t.push({code:"invalid_payload"});break}const n=d(e),i=l(n?.ieee??n?.ieee_address);if(!i){t.push({code:"invalid_payload",nodeKey:s});continue}const p=u("zha","zha",i);r.has(p)||r.set(p,{key:p,ieee:i,role:c(n?.device_type)}),_(o,t,s,p,e)}else null!=n?.neighbors&&t.push({code:"invalid_payload",nodeKey:s})}return{provider:"zha",instanceId:"zha",obtainedAt:n,freshness:"provider-cache",nodes:[...r.values()],links:[...o.values()],warnings:t}}function m(e,n){const t=d(e),i=l(t?.ieeeAddr??t?.ieee_address??t?.ieee??e);if(i)return i;for(const i of[t?.id,t?.networkAddress,t?.network_address,e])if(null!=i){const e=n.get(String(i));if(e)return e}return null}function v(e,t,i=Date.now()){const r=[],o=n(t)||"invalid",p=f(e)?function(e){let n=e;if("string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}const t=d(n),i=d(t?.data);if(n=i?.value??t?.value??t?.data??n,"string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}return n}(e):null,h=d(p),v=g(h?.nodes,1e3),b=g(h?.links,a);if(!v||!b)return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[],links:[],warnings:[{code:"invalid_payload"}]};const y=new Map,k=new Map;for(const e of v){const n=d(e),t=l(n?.ieeeAddr??n?.ieee_address??n?.ieee);if(!t){r.push({code:"invalid_payload"});continue}const i=u("z2m",o,t);y.set(i,{key:i,ieee:t,role:c(n?.type??n?.device_type),..."boolean"==typeof n?.failed?{available:!n.failed}:{}});for(const e of[n?.id,n?.networkAddress,n?.network_address])null!=e&&k.set(String(e),t)}const z=new Map;for(const e of b){const n=d(e),t=l(n?.sourceIeeeAddr)??m(n?.source,k),i=l(n?.targetIeeeAddr)??m(n?.target,k);if(!t||!i){r.push({code:"invalid_payload"});continue}const s=u("z2m",o,t),a=u("z2m",o,i);y.has(s)||y.set(s,{key:s,ieee:t,role:"unknown"}),y.has(a)||y.set(a,{key:a,ieee:i,role:"unknown"}),_(z,r,s,a,e)}return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[...y.values()],links:[...z.values()],warnings:r}}function b(e){if("string"!=typeof e)return null;const n=l(e);if(n)return n;const t=e.toLowerCase().match(/^zigbee2mqtt(?:_bridge)?_(.+)$/);return t?l(t[1]):null}function y(e,n){if(e.deviceId&&n.devices[e.deviceId])return[e.deviceId];const t=new Set;for(const[i,r]of Object.entries(n.devices)){const n=d(r);(Array.isArray(n?.identifiers)?n.identifiers:[]).some(n=>Array.isArray(n)&&b(n[1])===e.ieee)&&t.add(i)}if(t.size)return[...t];for(const i of Object.values(n.entities)){const n=d(i);"string"==typeof n?.device_id&&"string"==typeof n.unique_id&&(b(n.unique_id)===e.ieee&&t.add(n.device_id))}return[...t]}function k(e){return!e.hidden&&!e.virtual&&"ha_disabled"!==e.bindingStatus?.kind&&"orphaned"!==e.bindingStatus?.kind&&"unverified"!==e.bindingStatus?.kind}function z(e,n,t){const i=new Map,r=[];for(const o of e.nodes){if(!1===o.available){r.push({code:"provider_scan_failure",nodeKey:o.key});continue}const e=y(o,t);if(1!==e.length){r.push({code:"unmatched_device",nodeKey:o.key});continue}const s=e[0];let a=n.filter(e=>k(e)&&"device"===e.bindingKind&&e.bindingRef===s);a.length||(a=n.filter(e=>k(e)&&"entity"===e.bindingKind&&!!e.bindingRef&&t.entities[e.bindingRef]?.device_id===s)),1===a.length?i.set(o.key,{markerId:a[0].id,space:a[0].space}):r.push({code:a.length>1?"ambiguous_placement":"unmatched_device",nodeKey:o.key})}return{placements:i,warnings:r}}function A(e,n,t){return e.map(e=>({topology:e,placements:z(e,n,t).placements}))}function w(e,n,t){const i=new Map,r=new Set;let o=0;for(const{topology:s,placements:a}of e){const e=new Set([...a].filter(([,e])=>e.markerId===t&&e.space===n).map(([e])=>e));if(e.size)for(const d of s.links){const s=e.has(d.a),l=e.has(d.b);if(!s&&!l)continue;const u=a.get(s?d.b:d.a);if(!u||u.markerId===t){o++;continue}if(u.space!==n){r.add(u.markerId);continue}const c=s?d.aToB:d.bToA,p=i.get(u.markerId);(!p||void 0===p.lqi&&void 0!==c?.lqi)&&i.set(u.markerId,{neighborMarkerId:u.markerId,lqi:c?.lqi})}}return{lines:[...i.values()],remoteCount:r.size,omittedCount:o}}export{o as T,A as a,v as b,z as m,h as n,w as r,r as t}; diff --git a/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-runtime-BFjOXm55.js b/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js similarity index 93% rename from custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-runtime-BFjOXm55.js rename to custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js index 50c3acab..1be58f0b 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-runtime-BFjOXm55.js +++ b/custom_components/houseplan/frontend/houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{n as t,b as n}from"./zigbee-topology-x104QxRs.js";import{y as e}from"./houseplan-card-DcH_msOh.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function m(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function g(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,g=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await m(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await m(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{n as t,b as n}from"./zigbee-topology-D9L1ESh4.js";import{I as e}from"./houseplan-card-Csq_DR2Y.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function m(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function g(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,g=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await m(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await m(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot}; diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 4eaa406c..a867c73d 100644 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";try{await import("./houseplan-assets/houseplan-card-DcH_msOh.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";try{await import("./houseplan-assets/houseplan-card-Csq_DR2Y.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} diff --git a/demo/benchmark_large_house.mjs b/demo/benchmark_large_house.mjs index 79e0f36f..a2bb62ed 100644 --- a/demo/benchmark_large_house.mjs +++ b/demo/benchmark_large_house.mjs @@ -15,13 +15,15 @@ const warmups = Math.max(0, Math.min(5, Number(valueArg('warmups')) || 1)); const output = valueArg('output') ? resolve(valueArg('output')) : null; const targetRoot = resolve(valueArg('target-root') ?? '.'); const profile = valueArg('profile') ?? 'large-house-v1'; -if (!['large-house-v1', 'large-house-isometric-v1', 'large-house-plan-snap-v1'].includes(profile)) +if (!['large-house-v1', 'large-house-isometric-v1', 'large-house-plan-snap-v1', 'large-house-interaction-v1'].includes(profile)) throw new Error(`unknown large-house profile: ${profile}`); const isometric = profile === 'large-house-isometric-v1'; const planSnap = profile === 'large-house-plan-snap-v1'; +const interaction = profile === 'large-house-interaction-v1'; const requiresIsometric = isometric && existsSync(resolve(targetRoot, 'src/iso-projection.ts')); const requiresPlanSnap = planSnap && existsSync(resolve(targetRoot, 'src/plan-snap-overlay.ts')); const requiresWallFace = planSnap && existsSync(resolve(targetRoot, 'src/wall-face-graph.ts')); +const requiresInteraction = interaction && existsSync(resolve(targetRoot, 'src/live-viewport.ts')); const fixture = makeLargeHouseFixture(); if (planSnap) { for (const [floor, space] of fixture.config.spaces.entries()) { @@ -71,7 +73,7 @@ try { const measuredSample = iteration - warmups; const row = await page.evaluate(async ({ fixture, sample, cardContract, isometric, requiresIsometric, planSnap, requiresPlanSnap, - requiresWallFace, + requiresWallFace, interaction, requiresInteraction, }) => { const frame = () => new Promise((done) => requestAnimationFrame(() => requestAnimationFrame(done))); const until = async (predicate, timeout = 10000) => { @@ -151,10 +153,10 @@ try { subscribeEvents: async () => () => undefined, subscribeMessage: async () => () => undefined, }; - const hassFor = (states) => ({ + const hassBase = { language: 'en', locale: { language: 'en' }, user: { id: 'perf', name: 'Performance fixture', is_admin: true }, - devices: fixture.devices, entities: fixture.entities, areas: fixture.areas, states, + devices: fixture.devices, entities: fixture.entities, areas: fixture.areas, floors: { one: { floor_id: 'one', name: 'One', level: 0 }, two: { floor_id: 'two', name: 'Two', level: 1 }, @@ -178,7 +180,8 @@ try { localize: () => null, formatEntityState: (state) => state.state, config: { unit_system: { length: 'km' } }, - }); + }; + const hassFor = (states) => ({ ...hassBase, states }); const loadLongTasks = startLongTaskWindow(); const loadStarted = performance.now(); @@ -207,9 +210,33 @@ try { await frame(); const modelReadyMs = Number((performance.now() - loadStarted).toFixed(2)); await until(() => card._booting === false); + if (interaction && '_bootSoft' in card) await until(() => card._bootSoft === false); await frame(); const firstStableRenderMs = Number((performance.now() - loadStarted).toFixed(2)); const loadLongTaskResult = await loadLongTasks.stop(); + let fullRenderCount = 0; + let diagnosticsScanCount = 0; + let physicalPreflightCount = 0; + let physicalPreflightMs = 0; + const fullRenderReasons = []; + if (interaction) { + const renderBody = card._renderBody.bind(card); + card._renderBody = (...args) => { fullRenderCount++; return renderBody(...args); }; + const bindingStatus = card._bindingStatus.bind(card); + card._bindingStatus = (...args) => { diagnosticsScanCount++; return bindingStatus(...args); }; + const checkSpacePhysicalGeometry = card._checkSpacePhysicalGeometry.bind(card); + card._checkSpacePhysicalGeometry = (...args) => { + physicalPreflightCount++; + const started = performance.now(); + try { return checkSpacePhysicalGeometry(...args); } + finally { physicalPreflightMs += performance.now() - started; } + }; + const willUpdate = card.willUpdate.bind(card); + card.willUpdate = (changed) => { + fullRenderReasons.push([...changed.keys()].map(String)); + return willUpdate(changed); + }; + } const viewToggle = isometric ? await duration(async () => { if (typeof card._setProjection === 'function') { card._setProjection('flat'); @@ -237,6 +264,406 @@ try { await card.updateComplete; }); + let interactionDiagnostics = null; + let interactionTimings = null; + let interactionLongTasks = null; + const interactionSeries = interaction ? await duration(async () => { + const deltas = {}; + const timings = {}; + interactionLongTasks = {}; + const diagnosticsBefore = diagnosticsScanCount; + const reasonsBefore = fullRenderReasons.length; + const beforeIrrelevant = fullRenderCount; + const intakeBefore = card._hassSequence; + const irrelevantStarted = performance.now(); + let currentStates = nextStates; + for (let index = 0; index < 30; index++) { + currentStates = { + ...currentStates, + 'sensor.performance_unrelated': { + entity_id: 'sensor.performance_unrelated', state: `${sample}:${index}`, + }, + }; + card.hass = hassFor(currentStates); + } + await card.updateComplete; + await frame(); + timings.irrelevantHaTicksMs = performance.now() - irrelevantStarted; + deltas.irrelevantFullRenders = fullRenderCount - beforeIrrelevant; + deltas.irrelevantHassIntakes = card._hassSequence - intakeBefore; + + const relevantBefore = fullRenderCount; + const relevantStarted = performance.now(); + currentStates = { + ...currentStates, + [firstEntity]: { + ...currentStates[firstEntity], + state: currentStates[firstEntity].state === 'on' ? 'off' : 'on', + }, + }; + card.hass = hassFor(currentStates); + await card.updateComplete; + await frame(); + timings.relevantHaTickMs = performance.now() - relevantStarted; + deltas.relevantOutsideGestureFullRenders = fullRenderCount - relevantBefore; + + const stage = card.renderRoot.querySelector('.stage'); + const device = card.renderRoot.querySelector('[data-hp="device"]'); + const room = card.renderRoot.querySelector('[data-hp="room"]'); + const heavy = card.renderRoot.querySelector('.wallbodies'); + if (!stage || !device || !room || !heavy) + throw new Error('interaction profile has no stage/device/room/heavy scene'); + const rect = stage.getBoundingClientRect(); + const hoverBefore = fullRenderCount; + const hoverLongTasks = startLongTaskWindow(); + const hoverStarted = performance.now(); + device.dispatchEvent(new PointerEvent('pointerover', { + clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, + bubbles: true, composed: true, pointerId: 991, pointerType: 'mouse', isPrimary: true, + })); + for (let index = 0; index < 40; index++) { + device.dispatchEvent(new PointerEvent('pointermove', { + clientX: rect.left + 100 + index, clientY: rect.top + 100 + index / 2, + bubbles: true, composed: true, pointerId: 991, pointerType: 'mouse', isPrimary: true, + })); + if ((index + 1) % 20 === 0) { + await card.updateComplete; + await frame(); + } + } + const liveTip = card.renderRoot.querySelector('[data-hp-live-tip]'); + deltas.tooltipVisible = !!liveTip && !liveTip.hidden && !!liveTip.textContent?.trim(); + device.dispatchEvent(new PointerEvent('pointerleave', { + bubbles: false, composed: true, pointerId: 991, pointerType: 'mouse', isPrimary: true, + })); + room.dispatchEvent(new PointerEvent('pointerenter', { + clientX: rect.left + 300, clientY: rect.top + 300, + bubbles: false, composed: true, pointerId: 991, pointerType: 'mouse', isPrimary: true, + })); + for (let index = 0; index < 40; index++) { + room.dispatchEvent(new PointerEvent('pointermove', { + clientX: rect.left + 300 + index, clientY: rect.top + 300 + index / 2, + bubbles: true, composed: true, pointerId: 991, pointerType: 'mouse', isPrimary: true, + })); + if ((index + 1) % 20 === 0) { await card.updateComplete; await frame(); } + } + room.dispatchEvent(new PointerEvent('pointerleave', { + bubbles: false, composed: true, pointerId: 991, pointerType: 'mouse', isPrimary: true, + })); + for (let index = 0; index < 40; index++) { + stage.dispatchEvent(new PointerEvent('pointermove', { + clientX: rect.left + 20 + index, clientY: rect.top + 20, + bubbles: true, composed: true, pointerId: 991, pointerType: 'mouse', isPrimary: true, + })); + if ((index + 1) % 20 === 0) { await card.updateComplete; await frame(); } + } + await card.updateComplete; + timings.hoverSeriesMs = performance.now() - hoverStarted; + deltas.hoverFullRenders = fullRenderCount - hoverBefore; + deltas.hoverTargets = ['device', 'room', 'miss']; + interactionLongTasks.hoverSeries = await hoverLongTasks.stop(); + + const cameraSvg = card.renderRoot.querySelector('[data-hp-live-viewbox="camera"], [data-hp-live-viewbox="floor"], .zoomwrap > svg'); + const initialViewBox = cameraSvg?.getAttribute('viewBox') || ''; + const panLongTasks = startLongTaskWindow(); + const panStarted = performance.now(); + const panMoveRenders = []; + const panTerminalRenders = []; + let relevantDuringGestureFullRenders = 0; + let relevantDuringGestureIntakes = 0; + for (let dragIndex = 0; dragIndex < 4; dragIndex++) { + const pointerId = 992 + dragIndex; + stage.dispatchEvent(new PointerEvent('pointerdown', { + clientX: rect.left + 200, clientY: rect.top + 200, button: 0, buttons: 1, + bubbles: true, composed: true, pointerId, pointerType: 'mouse', isPrimary: true, + })); + await card.updateComplete; + const panMoveBefore = fullRenderCount; + for (let index = 0; index < 20; index++) { + stage.dispatchEvent(new PointerEvent('pointermove', { + clientX: rect.left + 205 + index * 3, clientY: rect.top + 202 + index, + button: 0, buttons: 1, bubbles: true, composed: true, + pointerId, pointerType: 'mouse', isPrimary: true, + })); + if (dragIndex === 0 && index === 10) { + const deferredBefore = fullRenderCount; + const deferredIntakeBefore = card._hassSequence; + for (let tick = 0; tick < 3; tick++) { + currentStates = { + ...currentStates, + [firstEntity]: { ...currentStates[firstEntity], state: `gesture-${tick}` }, + }; + card.hass = hassFor(currentStates); + } + await card.updateComplete; + relevantDuringGestureFullRenders += fullRenderCount - deferredBefore; + relevantDuringGestureIntakes += card._hassSequence - deferredIntakeBefore; + } + if ((index + 1) % 10 === 0) { + await card.updateComplete; + await frame(); + } + } + const beforeTerminal = fullRenderCount; + panMoveRenders.push(beforeTerminal - panMoveBefore); + stage.dispatchEvent(new PointerEvent('pointerup', { + clientX: rect.left + 265, clientY: rect.top + 222, button: 0, buttons: 0, + bubbles: true, composed: true, pointerId, pointerType: 'mouse', isPrimary: true, + })); + await card.updateComplete; + await frame(); + panTerminalRenders.push(fullRenderCount - beforeTerminal); + } + timings.panSeriesMs = performance.now() - panStarted; + interactionLongTasks.panSeries = await panLongTasks.stop(); + const movedViewBox = cameraSvg?.getAttribute('viewBox') || ''; + const currentView = card._viewOr(card._baseVb()); + const currentDevice = card._devices.find((item) => item.id === device.dataset.id); + const position = currentDevice ? card._pos(currentDevice) : null; + const scene = position ? card._scenePoint([position.x, position.y]) : null; + const deviceRect = device.getBoundingClientRect(); + const expectedX = scene ? rect.left + ((scene[0] - currentView.x) / currentView.w) * rect.width : 0; + const expectedY = scene ? rect.top + ((scene[1] - currentView.y) / currentView.h) * rect.height : 0; + const overlayErrorPx = scene ? Math.hypot( + deviceRect.left + deviceRect.width / 2 - expectedX, + deviceRect.top + deviceRect.height / 2 - expectedY, + ) : Infinity; + + const cameraLongTasks = startLongTaskWindow(); + const cameraStarted = performance.now(); + const cameraBefore = fullRenderCount; + const pinchPointerA = 1001, pinchPointerB = 1002; + stage.dispatchEvent(new PointerEvent('pointerdown', { + clientX: rect.left + 360, clientY: rect.top + 300, button: 0, buttons: 1, + bubbles: true, composed: true, pointerId: pinchPointerA, + pointerType: 'touch', isPrimary: true, + })); + stage.dispatchEvent(new PointerEvent('pointerdown', { + clientX: rect.left + 460, clientY: rect.top + 300, button: 0, buttons: 1, + bubbles: true, composed: true, pointerId: pinchPointerB, + pointerType: 'touch', isPrimary: false, + })); + await card.updateComplete; + const pinchMoveBefore = fullRenderCount; + for (let index = 0; index < 20; index++) { + stage.dispatchEvent(new PointerEvent('pointermove', { + clientX: rect.left + 360 - index, clientY: rect.top + 300, + button: 0, buttons: 1, bubbles: true, composed: true, + pointerId: pinchPointerA, pointerType: 'touch', isPrimary: true, + })); + stage.dispatchEvent(new PointerEvent('pointermove', { + clientX: rect.left + 460 + index, clientY: rect.top + 300, + button: 0, buttons: 1, bubbles: true, composed: true, + pointerId: pinchPointerB, pointerType: 'touch', isPrimary: false, + })); + if ((index + 1) % 5 === 0) await frame(); + } + const pinchTerminalBefore = fullRenderCount; + for (const pointerId of [pinchPointerA, pinchPointerB]) { + stage.dispatchEvent(new PointerEvent('pointerup', { + clientX: rect.left + 410, clientY: rect.top + 300, + button: 0, buttons: 0, bubbles: true, composed: true, + pointerId, pointerType: 'touch', isPrimary: pointerId === pinchPointerA, + })); + } + await card.updateComplete; + await frame(); + const pinchTerminalRenders = fullRenderCount - pinchTerminalBefore; + for (let index = 0; index < 4; index++) { + stage.dispatchEvent(new WheelEvent('wheel', { + deltaY: index % 2 ? 120 : -120, + clientX: rect.left + rect.width / 2, + clientY: rect.top + rect.height / 2, + bubbles: true, cancelable: true, + })); + } + await until(() => !card._cameraTransition.active); + await card.updateComplete; + await frame(); + timings.cameraSeriesMs = performance.now() - cameraStarted; + interactionLongTasks.cameraSeries = await cameraLongTasks.stop(); + deltas.cameraMoveFullRenders = pinchTerminalBefore - pinchMoveBefore; + deltas.cameraTerminalFullRenders = pinchTerminalRenders; + deltas.cameraFullRenders = fullRenderCount - cameraBefore; + + const editorLongTaskWindows = []; + const editorConfigBefore = JSON.stringify(card._serverCfg); + const editorCallsBefore = wsCalls; + const editorPaintBefore = card._liveEditorPaintCount || 0; + let editorElapsed = 0; + const editorMoveRenders = []; + const editorTerminalRenders = []; + + card._setMode('plan', false); + card._tool = 'draw'; + card._path = [[100, 100]]; + card.requestUpdate(); + await card.updateComplete; + await until(() => !card._modeTransitionBusy && card._continuity.state === 'steady'); + for (let index = 0; index < 5; index++) await frame(); + let editorStage = card.renderRoot.querySelector('.stage'); + let editorRect = editorStage.getBoundingClientRect(); + let editorView = card._viewOr(card._baseVb()); + const fromScene = (x, y) => ({ + clientX: editorRect.left + ((x - editorView.x) / editorView.w) * editorRect.width, + clientY: editorRect.top + ((y - editorView.y) / editorView.h) * editorRect.height, + }); + let editorPartLongTasks = startLongTaskWindow(); + let partStarted = performance.now(); + let editorMoveBefore = fullRenderCount; + for (let index = 0; index < 40; index++) { + editorStage.dispatchEvent(new PointerEvent('pointermove', { + ...fromScene(110 + index * 2, 110 + index), + bubbles: true, composed: true, pointerId: 1101, + pointerType: 'mouse', isPrimary: true, + })); + if ((index + 1) % 10 === 0) await frame(); + } + let editorTerminalBefore = fullRenderCount; + editorMoveRenders.push(editorTerminalBefore - editorMoveBefore); + card._cursorPt = null; + card._path = []; + card.requestUpdate(); + await card.updateComplete; + await frame(); + editorTerminalRenders.push(fullRenderCount - editorTerminalBefore); + editorElapsed += performance.now() - partStarted; + editorLongTaskWindows.push(await editorPartLongTasks.stop()); + + card._tool = 'resize'; + card.requestUpdate(); + await card.updateComplete; + const resizeRoom = card._rszRooms()[0]; + const resizeEvent = { + pointerId: 1102, stopPropagation: () => undefined, + preventDefault: () => undefined, target: null, + }; + card._rszEdgeDown(resizeEvent, resizeRoom.id, 1); + const resizePlan = card._resize?.plan || card._rszDrag?.plan; + if (!resizePlan) throw new Error('interaction resize plan was not created'); + const resizePreflightBefore = physicalPreflightCount; + const resizePreflightMsBefore = physicalPreflightMs; + editorStage = card.renderRoot.querySelector('.stage'); + editorRect = editorStage.getBoundingClientRect(); + editorView = card._viewOr(card._baseVb()); + // A browser delivers pointerdown and pointermove as separate tasks. + // Keep the synthetic harness from attributing both handlers to one + // impossible long task while still timing every move-side preflight. + await new Promise((done) => setTimeout(done, 0)); + editorPartLongTasks = startLongTaskWindow(); + partStarted = performance.now(); + editorMoveBefore = fullRenderCount; + for (let index = 0; index < 40; index++) { + const target = [ + resizePlan.a[0] + resizePlan.n[0] * card._gridPitch * (1 + index / 40), + resizePlan.a[1] + resizePlan.n[1] * card._gridPitch * (1 + index / 40), + ]; + card._rszMove({ + ...resizeEvent, + clientX: editorRect.left + ((target[0] - editorView.x) / editorView.w) * editorRect.width, + clientY: editorRect.top + ((target[1] - editorView.y) / editorView.h) * editorRect.height, + }); + if ((index + 1) % 10 === 0) await frame(); + } + editorTerminalBefore = fullRenderCount; + editorMoveRenders.push(editorTerminalBefore - editorMoveBefore); + deltas.resizeLivePreflightChecks = physicalPreflightCount - resizePreflightBefore; + deltas.resizeLivePreflightMs = physicalPreflightMs - resizePreflightMsBefore; + card._rszCancelDrag(); + await card.updateComplete; + await frame(); + editorTerminalRenders.push(fullRenderCount - editorTerminalBefore); + editorElapsed += performance.now() - partStarted; + editorLongTaskWindows.push(await editorPartLongTasks.stop()); + + card._setMode('decor', false); + card._decorTool = 'select'; + const decorShape = card._decorList.find((shape) => + shape.kind === 'furniture' || shape.kind === 'rect' || shape.kind === 'ellipse'); + if (!decorShape) throw new Error('interaction profile has no transformable decor'); + card._decorSel = decorShape.id; + card.requestUpdate(); + await card.updateComplete; + await until(() => !card._modeTransitionBusy && card._continuity.state === 'steady'); + for (let index = 0; index < 5; index++) await frame(); + card._dtMeasure(); + await card.updateComplete; + editorStage = card.renderRoot.querySelector('.stage'); + editorRect = editorStage.getBoundingClientRect(); + editorView = card._viewOr(card._baseVb()); + const box = card._decorBoxOf(decorShape); + const cornerX = box.x + box.w, cornerY = box.y + box.h; + const decorPointer = (x, y) => ({ + pointerId: 1103, pointerType: 'mouse', shiftKey: false, + clientX: editorRect.left + ((x - editorView.x) / editorView.w) * editorRect.width, + clientY: editorRect.top + ((y - editorView.y) / editorView.h) * editorRect.height, + stopPropagation: () => undefined, preventDefault: () => undefined, + currentTarget: null, target: null, + }); + card._dtStart(decorPointer(cornerX, cornerY), 'scale', [1, 1]); + await card.updateComplete; + await frame(); + editorPartLongTasks = startLongTaskWindow(); + partStarted = performance.now(); + editorMoveBefore = fullRenderCount; + for (let index = 0; index < 40; index++) { + card._dtMove(decorPointer(cornerX + index, cornerY + index / 2)); + if ((index + 1) % 10 === 0) await frame(); + } + editorTerminalBefore = fullRenderCount; + editorMoveRenders.push(editorTerminalBefore - editorMoveBefore); + card._cancelDecorGesture(); + await card.updateComplete; + await frame(); + editorTerminalRenders.push(fullRenderCount - editorTerminalBefore); + editorElapsed += performance.now() - partStarted; + editorLongTaskWindows.push(await editorPartLongTasks.stop()); + timings.editorSeriesMs = editorElapsed; + interactionLongTasks.editorSeries = { + supported: editorLongTaskWindows.every((window) => window.supported), + count: editorLongTaskWindows.reduce((sum, window) => sum + window.count, 0), + maxMs: Math.max(0, ...editorLongTaskWindows.map((window) => window.maxMs)), + totalMs: editorLongTaskWindows.reduce((sum, window) => sum + window.totalMs, 0), + }; + deltas.editorMoveFullRenders = editorMoveRenders; + deltas.editorTerminalFullRenders = editorTerminalRenders; + deltas.editorLivePaints = (card._liveEditorPaintCount || 0) - editorPaintBefore; + deltas.editorConfigStable = JSON.stringify(card._serverCfg) === editorConfigBefore; + deltas.editorWsWrites = wsCalls - editorCallsBefore; + deltas.editorLongTaskParts = editorLongTaskWindows; + + card._setMode('view', false); + await card.updateComplete; + await frame(); + deltas.panMoveFullRenders = panMoveRenders; + deltas.terminalFullRenders = panTerminalRenders; + deltas.relevantDuringGestureFullRenders = relevantDuringGestureFullRenders; + deltas.relevantDuringGestureIntakes = relevantDuringGestureIntakes; + deltas.viewBoxMoved = !!initialViewBox && initialViewBox !== movedViewBox; + deltas.overlayErrorPx = Number(overlayErrorPx.toFixed(2)); + deltas.heavyNodeStable = card.renderRoot.querySelector('.wallbodies') === heavy; + deltas.diagnosticsScans = diagnosticsScanCount - diagnosticsBefore; + deltas.fullRenderReasons = fullRenderReasons.slice(reasonsBefore); + interactionTimings = timings; + interactionDiagnostics = { supported: requiresInteraction, ...deltas }; + if (requiresInteraction && ( + deltas.irrelevantFullRenders !== 0 || deltas.irrelevantHassIntakes !== 30 + || deltas.relevantOutsideGestureFullRenders !== 1 || deltas.hoverFullRenders !== 0 + || deltas.hoverTargets.join(',') !== 'device,room,miss' + || deltas.panMoveFullRenders.some((count) => count !== 0) + || deltas.terminalFullRenders.some((count) => count !== 1) + || deltas.relevantDuringGestureFullRenders !== 0 || !deltas.tooltipVisible + || deltas.relevantDuringGestureIntakes !== 3 + || deltas.cameraMoveFullRenders !== 0 || deltas.cameraTerminalFullRenders !== 1 + || deltas.editorMoveFullRenders.some((count) => count !== 0) + || deltas.editorTerminalFullRenders.some((count) => count > 1) + || deltas.resizeLivePreflightChecks < 1 + || deltas.editorLivePaints < 3 || !deltas.editorConfigStable || deltas.editorWsWrites !== 0 + || !deltas.viewBoxMoved || deltas.overlayErrorPx > 1 || !deltas.heavyNodeStable + || deltas.diagnosticsScans !== 0 + )) throw new Error(`interaction structural contract failed: ${JSON.stringify(interactionDiagnostics)}`); + }) : null; + let planSnapDiagnostics = null; const planSnapPointer = planSnap ? await duration(async () => { card._setMode('plan'); @@ -433,6 +860,11 @@ try { planSnapPointerMs: planSnapPointer.ms, planSnapDiagnostics, } : {}), + ...(interactionSeries ? { + interactionSeriesMs: interactionSeries.ms, + ...interactionTimings, + interactionDiagnostics, + } : {}), spaceSwitchMs: spaceSwitch.ms, stateUpdateMs: stateUpdate.ms, resizePreviewMs: resizePreview.ms, @@ -443,6 +875,7 @@ try { load: loadLongTaskResult, ...(viewToggle ? { viewToggle: viewToggle.longTasks } : {}), ...(planSnapPointer ? { planSnapPointer: planSnapPointer.longTasks } : {}), + ...(interactionSeries ? interactionLongTasks || {} : {}), spaceSwitch: spaceSwitch.longTasks, stateUpdate: stateUpdate.longTasks, resizePreview: resizePreview.longTasks, @@ -462,6 +895,7 @@ try { }, { fixture, sample: measuredSample, cardContract: LARGE_HOUSE_CARD_CONTRACT, isometric, requiresIsometric, planSnap, requiresPlanSnap, requiresWallFace, + interaction, requiresInteraction, }); if (measuredSample >= 0) rows.push(row); } @@ -475,6 +909,9 @@ const metricNames = [ ]; if (isometric) metricNames.splice(2, 0, 'viewToggleMs'); if (planSnap) metricNames.splice(2, 0, 'planSnapPointerMs'); +if (interaction) metricNames.splice(2, 0, + 'interactionSeriesMs', 'hoverSeriesMs', 'panSeriesMs', 'cameraSeriesMs', + 'editorSeriesMs', 'irrelevantHaTicksMs', 'relevantHaTickMs'); const report = { schema: 2, profile, diff --git a/demo/performance/README.md b/demo/performance/README.md index 78e3ed41..4a02db4e 100644 --- a/demo/performance/README.md +++ b/demo/performance/README.md @@ -25,6 +25,24 @@ websocket traffic change. Its dedicated budget retains every original timing, heap and cache ceiling and adds the measured pointer series plus a one-entry snap-geometry cache cap. Exact-SHA Linux output is the only gate evidence. +Issue #451 adds `large-house-interaction-v1`. It dispatches 120 real +device/room/free-background hover moves, four 20-move pans, pinch/wheel camera input, three editor +drag series and 30 unrelated HA snapshots. It also injects relevant snapshots +inside and outside a gesture and counts heavy `_renderBody()` passes plus full +marker-binding diagnostic scans. Current bundles fail inside the runner unless +hover, editor moves and unrelated ticks perform zero full renders, a relevant +tick is deferred during pan, each terminal event reconciles at most one full +frame, the heavy wall DOM remains identical, and the projected HTML device +layer stays within one CSS pixel of its SVG scene position. Older comparison +bundles still produce timing baselines; structural assertions become mandatory +only when the target source contains the live viewport implementation. Its +dedicated budget preserves every existing large-house ceiling and adds the +individual interaction timing gates. + +Structural assertions are evaluated independently of timing budgets: a fast +run still fails when an interaction performs an unexpected full render or +changes the heavy scene DOM. + The runner records seven measured samples after one discarded warm-up. With this intentionally small CI sample, the nearest-rank `p95` is the observed maximum; reports keep the conventional field name but should be read as a @@ -126,6 +144,7 @@ Build and copy a fresh demo bundle first, then run: ```bash npm run benchmark:large-house -- --samples=7 --warmups=1 --output=artifacts/performance/local.json npm run benchmark:large-house-plan-snap -- --samples=7 --warmups=1 --output=artifacts/performance/plan-snap-local.json +npm run benchmark:large-house-interaction -- --samples=7 --warmups=1 --output=artifacts/performance/interaction-local.json ``` A local report is diagnostic only; it cannot replace the CI comparison. diff --git a/demo/performance/budgets-large-house-interaction.json b/demo/performance/budgets-large-house-interaction.json new file mode 100644 index 00000000..8ccfcd10 --- /dev/null +++ b/demo/performance/budgets-large-house-interaction.json @@ -0,0 +1,139 @@ +{ + "schema": 1, + "profile": "large-house-interaction-v1", + "minimumSamples": 7, + "timings": { + "modelReadyMs": { + "stat": "median", + "maxRegressionRatio": 0.3, + "noiseAllowanceMs": 200, + "hardMaxMs": 2500 + }, + "firstStableRenderMs": { + "stat": "median", + "maxRegressionRatio": 0.3, + "noiseAllowanceMs": 250, + "hardMaxMs": 3000 + }, + "interactionSeriesMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 150, + "hardMaxMs": 3000 + }, + "hoverSeriesMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 60, + "hardMaxMs": 500 + }, + "panSeriesMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 60, + "hardMaxMs": 500 + }, + "cameraSeriesMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 60, + "hardMaxMs": 500 + }, + "editorSeriesMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 100, + "hardMaxMs": 750 + }, + "irrelevantHaTicksMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 40, + "hardMaxMs": 250 + }, + "relevantHaTickMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 40, + "hardMaxMs": 250 + }, + "spaceSwitchMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 75, + "hardMaxMs": 1500 + }, + "stateUpdateMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 75, + "hardMaxMs": 1000 + }, + "resizePreviewMs": { + "stat": "median", + "maxRegressionRatio": 0.4, + "noiseAllowanceMs": 150, + "hardMaxMs": 2000 + }, + "panZoomMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 60, + "hardMaxMs": 500 + }, + "settingsDialogMs": { + "stat": "median", + "maxRegressionRatio": 0.5, + "noiseAllowanceMs": 100, + "hardMaxMs": 1000 + }, + "switchCycleMs": { + "stat": "median", + "maxRegressionRatio": 0.35, + "noiseAllowanceMs": 250, + "hardMaxMs": 7000 + } + }, + "longTasks": { + "maxSingleMs": 3000, + "maxSingleRegressionRatio": 0.3, + "maxSingleNoiseAllowanceMs": 250, + "maxCountP95": 30, + "maxCountRegressionRatio": 0.35, + "countNoiseAllowance": 3, + "maxTotalP95Ms": 12000, + "maxTotalRegressionRatio": 0.3, + "noiseAllowanceMs": 150 + }, + "longTaskWindows": { + "hoverSeries": { "maxSingleMs": 150, "maxCountP95": 3, "maxTotalP95Ms": 300 }, + "panSeries": { "maxSingleMs": 150, "maxCountP95": 3, "maxTotalP95Ms": 300 }, + "cameraSeries": { "maxSingleMs": 150, "maxCountP95": 3, "maxTotalP95Ms": 300 }, + "editorSeries": { "maxSingleMs": 150, "maxCountP95": 3, "maxTotalP95Ms": 300 } + }, + "heap": { + "required": true, + "hardMaxGrowthBytes": 67108864, + "maxRegressionRatio": 0.75, + "noiseAllowanceBytes": 16777216 + }, + "cacheEntries": { + "cleanFloor": 100, + "glowClip": 200, + "wallUnion": 1, + "openingTunnel": 1, + "openingWallIndex": 1, + "planSnapGeometry": 1, + "wallFaceGraph": 4 + }, + "cacheGrowth": { + "cleanFloor": 0, + "glowClip": 0, + "wallUnion": 0, + "openingTunnel": 0, + "openingWallIndex": 0, + "planSnapGeometry": 0, + "wallFaceGraph": 0 + }, + "renderedDevices": 200 +} diff --git a/demo/performance/card-contract.mjs b/demo/performance/card-contract.mjs index 4a24e3a8..12e63380 100644 --- a/demo/performance/card-contract.mjs +++ b/demo/performance/card-contract.mjs @@ -17,22 +17,41 @@ export const LARGE_HOUSE_CARD_CONTRACT = Object.freeze({ label: 'large-house-v1', methods: Object.freeze([ '_baseVb', + '_bindingStatus', + '_cancelDecorGesture', + '_checkSpacePhysicalGeometry', + '_decorBoxOf', + '_dtMeasure', + '_dtMove', + '_dtStart', '_openSettingsDialog', '_pickSpace', + '_pos', + '_renderBody', '_rszCancelDrag', '_rszEdgeDown', '_rszMove', '_rszRooms', '_setMode', + '_scenePoint', '_viewOr', ]), fields: Object.freeze([ '_booting', + '_bootSoft', + '_cameraTransition', ...CACHE_FIELDS, + '_continuity', + '_cursorPt', + '_decorList', + '_decorSel', + '_decorTool', '_devices', '_gridPitch', + '_hassSequence', '_loadOk', '_model', + '_modeTransitionBusy', '_path', '_serverCfg', '_settingsDialog', @@ -42,7 +61,7 @@ export const LARGE_HOUSE_CARD_CONTRACT = Object.freeze({ // checks these members only when the target source tree supports Stage 1. optionalFields: Object.freeze([ '_activeDraftId', '_draftSegmentCms', '_isoGeometryCache', '_offerWallFaces', - '_onLabsSnapshot', '_planSnapGeometryCache', '_roomDialog', '_setProjection', + '_liveEditorPaintCount', '_onLabsSnapshot', '_planSnapGeometryCache', '_roomDialog', '_setProjection', '_wallFaceBatch', '_wallFaceGraphCache', ]), // #380: v1.68.1 owns the same resize session directly on the card; newer @@ -54,12 +73,19 @@ export const LARGE_HOUSE_CARD_CONTRACT = Object.freeze({ ]), fieldTypes: Object.freeze({ _booting: 'boolean', + _bootSoft: 'boolean', + _cameraTransition: 'object', _cleanFloorCache: 'map', _devices: 'array', + _continuity: 'object', + _decorList: 'array', + _decorTool: 'string', _draftSegmentCms: 'array', _glowClipCache: 'map', _gridPitch: 'number', + _hassSequence: 'number', _loadOk: 'boolean', + _liveEditorPaintCount: 'number', _model: 'array', _onLabsSnapshot: 'function', _offerWallFaces: 'function', diff --git a/demo/performance/evaluate.mjs b/demo/performance/evaluate.mjs index 22f485a1..0a2ba786 100644 --- a/demo/performance/evaluate.mjs +++ b/demo/performance/evaluate.mjs @@ -143,6 +143,20 @@ export const evaluatePerformanceBudget = ({ candidate, baseline, budgets, absolu }, )); + for (const [windowName, budget] of Object.entries(budgets.longTaskWindows ?? {})) { + const windows = candidate.rows.map((row) => row.longTasks?.[windowName]); + const available = windows.every((window) => window?.supported === true); + checks.push({ + id: `longTask.${windowName}.available`, actual: available ? 1 : 0, limit: 1, pass: available, + }); + const maxSingle = available ? Math.max(...windows.map((window) => window.maxMs)) : NaN; + const countP95 = available ? percentile(windows.map((window) => window.count), 0.95) : NaN; + const totalP95 = available ? percentile(windows.map((window) => window.totalMs), 0.95) : NaN; + checks.push(makeCheck(`longTask.${windowName}.maxSingleMs`, maxSingle, budget.maxSingleMs)); + checks.push(makeCheck(`longTask.${windowName}.countP95`, countP95, budget.maxCountP95)); + checks.push(makeCheck(`longTask.${windowName}.totalP95Ms`, totalP95, budget.maxTotalP95Ms)); + } + const candidateHeap = candidate.rows .map((row) => row.heapGrowthBytes) .filter(finite) diff --git a/demo/smoke_bg_color.mjs b/demo/smoke_bg_color.mjs index 90ff9b9b..c5eb8238 100644 --- a/demo/smoke_bg_color.mjs +++ b/demo/smoke_bg_color.mjs @@ -325,6 +325,7 @@ const drawn = await page.evaluate(async () => { }, options: { minDim: 1, eps: 0.01 }, rooms: resizeRooms, openings: [], snapshotIdentity: snapshot, before: JSON.parse(snapshot), wallUnionBefore: null, + epochBefore: c._cfgEpoch, }); c._resize.move({ pointerId: 912, point: [687.5, 250], step: 0.5, snap: (point) => point, diff --git a/demo/smoke_decor.mjs b/demo/smoke_decor.mjs index 074c1263..c56a18f3 100644 --- a/demo/smoke_decor.mjs +++ b/demo/smoke_decor.mjs @@ -10,6 +10,11 @@ const res = await page.evaluate(async () => { while (c._modeTransitionBusy && performance.now() - started < 1500); await c.updateComplete; }; + const settleLive = async () => { + await Promise.resolve(); + await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))); + await c.updateComplete; + }; const esc = async () => { window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); await c.updateComplete; }; // 1) третья вкладка есть const tabs = [...sr().querySelectorAll('.modetab')]; @@ -272,10 +277,10 @@ const res = await page.evaluate(async () => { const vw = () => c._viewOr(c._baseVb()); c._decorTool = 'line'; c._decorDraft = { kind: 'line', a: [g * 4, g * 20], b: [g * 4, g * 20], pid: 3 }; - await c.updateComplete; + await settleLive(); out.noBadgeBeforeTheDrag = !label(); // нулевая длина — молчим c._decorDraft = { ...c._decorDraft, b: [g * 16, g * 20] }; // 12 клеток вправо - await c.updateComplete; + await settleLive(); out.lineBadgeShown = !!label(); // 12 клеток × 5 см = 0.60 m, угол 0° — ровно то, что пишет плашка стены out.lineBadgeText = txt(); @@ -287,12 +292,12 @@ const res = await page.evaluate(async () => { out.badgeGreenOnAxis = !!label() && label().classList.contains('on45'); // косой отрезок 3-4-5: длина 5 клеток, угол 53.1° — не кратен 45° c._decorDraft = { kind: 'line', a: [g * 4, g * 20], b: [g * 7, g * 24], pid: 3 }; - await c.updateComplete; + await settleLive(); out.obliqueBadge = txt() === fmt(g * 5) + ' · 53.1°'; out.obliqueNotGreen = !!label() && !label().classList.contains('on45'); // прямоугольник показывает габарит и чистую площадь c._decorDraft = { kind: 'rect', a: [g * 4, g * 20], b: [g * 12, g * 26], pid: 3 }; - await c.updateComplete; + await settleLive(); out.rectBadgeHasSizeAndArea = txt().startsWith(fmt(g * 8) + ' × ' + fmt(g * 6) + ' · ') && /(?:m²|ft²)$/.test(txt()); // отпустили — плашки нет diff --git a/demo/smoke_decor_text.mjs b/demo/smoke_decor_text.mjs index 1bff1065..587fd116 100644 --- a/demo/smoke_decor_text.mjs +++ b/demo/smoke_decor_text.mjs @@ -27,9 +27,15 @@ const res = await page.evaluate(async () => { while (c._modeTransitionBusy && performance.now() - started < 1500); await c.updateComplete; }; + const settleLive = async () => { + await Promise.resolve(); + await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))); + await c.updateComplete; + }; // null-safe: на сборке ДО этих правок у надписи нет ни data-id, ни tspan — // смок должен показать список провалов, а не упасть с исключением - const el = (id) => sr().querySelector(`.decorlayer text.dtext[data-id="${id}"]`); + const el = (id) => sr().querySelector(`[data-hp-live-editor] .decorlayer text.dtext[data-id="${id}"]`) + || sr().querySelector(`.decorlayer text.dtext[data-id="${id}"]`); const attr = (id, name) => { const e = el(id); return e ? e.getAttribute(name) : null; }; const tspans = (id) => { const e = el(id); return e ? [...e.querySelectorAll('tspan')] : []; }; const stageEl = () => sr().querySelector('.stage'); @@ -101,7 +107,7 @@ const res = await page.evaluate(async () => { out.noFrameWithoutSelection = !sr().querySelector('.dtframe'); c._decorSel = 'dtm'; c.requestUpdate(); await c.updateComplete; await sleep(60); await c.updateComplete; - const frame = () => sr().querySelector('.dtframe'); + const frame = () => sr().querySelector('[data-hp-live-editor] .dtframe') || sr().querySelector('.dtframe'); out.frameOnSelection = !!frame(); out.fourCornersAndRotate = !!frame() && frame().querySelectorAll('.dthandle').length === 5 @@ -147,7 +153,7 @@ const res = await page.evaluate(async () => { ev('pointerdown', corner, ax + 40, ay2); out.dragStarted = !!c._dtDrag && c._dtDrag.kind === 'scale'; ev('pointermove', stageEl(), ax + 80, ay2); - await c.updateComplete; + await settleLive(); const scaled = c._decorList.find((x) => x.id === 'dtm'); out.cornerStoresPhysicalSize = scaled.scale === undefined && scaled.size === undefined && Math.abs((scaled.size_cm || 0) - mediumSizeCm0 * 2) < 0.01; diff --git a/demo/smoke_drag_bounds.mjs b/demo/smoke_drag_bounds.mjs index f2469ba6..4ef5a5a8 100644 --- a/demo/smoke_drag_bounds.mjs +++ b/demo/smoke_drag_bounds.mjs @@ -131,6 +131,7 @@ const out = await page.evaluate(async () => { beginDeviceDrag(dev3, r0, 13); c._pointerMove(mk(2.5 * NORM_W + halfStep - r0.x, 2.5 * NORM_W - r0.y, 400, 400, { shiftKey: true, pointerId: 13 }), dev3); + await Promise.resolve(); o.shiftStillSnapsToTheNodes = onGrid(c._layout[dev3.id].x); c._cancelDeviceDrag(); diff --git a/demo/smoke_furniture.mjs b/demo/smoke_furniture.mjs index 3b554705..070ee8fa 100644 --- a/demo/smoke_furniture.mjs +++ b/demo/smoke_furniture.mjs @@ -23,8 +23,14 @@ const res = await page.evaluate(async () => { const c = window.__card; const sr = () => c.shadowRoot || c.renderRoot; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + const settleLive = async () => { + await Promise.resolve(); + await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))); + await c.updateComplete; + }; const stageEl = () => sr().querySelector('.stage'); - const el = (id) => (id ? sr().querySelector(`.decorlayer [data-id="${id}"]`) : null); + const el = (id) => (id ? sr().querySelector(`[data-hp-live-editor] .decorlayer [data-id="${id}"]`) + || sr().querySelector(`.decorlayer [data-id="${id}"]`) : null); const attr = (id, n) => { const e = el(id); return e ? e.getAttribute(n) : null; }; const toScreen = (x, y) => { const r = stageEl().getBoundingClientRect(); @@ -46,6 +52,7 @@ const res = await page.evaluate(async () => { const cmToUnits = (cm) => (cm / CELL) * PITCH; const near = (a, b, tol) => Number.isFinite(a) && Number.isFinite(b) && Math.abs(a - b) <= tol; const pe = (id) => { const e = el(id); return e ? getComputedStyle(e).pointerEvents : null; }; + const box = () => c._decorBoxOf(sofaNow()); sr().querySelectorAll('.modetab')[2].click(); await c.updateComplete; c._curSpaceCfg.decor = []; @@ -148,7 +155,7 @@ const res = await page.evaluate(async () => { redo: c._geometryHistory.redoName, }; ev('pointermove', stageEl(), 300, 300, { pointerType: 'mouse' }); - await c.updateComplete; + await settleLive(); const firstPreview = c._editorRuntime?._furniturePreviewPlacement() ? JSON.parse(JSON.stringify(c._editorRuntime._furniturePreviewPlacement())) : null; const firstTransform = ghost()?.getAttribute('transform'); @@ -176,7 +183,7 @@ const res = await page.evaluate(async () => { stageEl().dispatchEvent(new PointerEvent('pointerleave', { bubbles: true, composed: true, pointerId: 11, pointerType: 'mouse', })); - await c.updateComplete; + await settleLive(); out.pointerLeaveClearsPreview = !ghost() && c._furnPreviewInput === null; // Unknown/stale symbols fail dark: neither an invisible ghost nor a decor @@ -184,7 +191,7 @@ const res = await page.evaluate(async () => { c._furnPalette = { symbol: 'future_unknown_symbol', w: 180, h: 90 }; await c.updateComplete; ev('pointermove', stageEl(), 300, 300, { pointerType: 'mouse' }); - await c.updateComplete; + await settleLive(); const beforeUnknown = c._decorList.length; ev('pointerdown', stageEl(), 300, 300, { pointerType: 'mouse' }); await c.updateComplete; @@ -196,7 +203,7 @@ const res = await page.evaluate(async () => { // ================= 4. размещение в реальном размере ====================== // середина комнаты r1 (40..550 × 140..580) — до стен дальше порога магнита ev('pointermove', stageEl(), 300, 300, { pointerType: 'mouse' }); - await c.updateComplete; + await settleLive(); const committedPreview = c._editorRuntime?._furniturePreviewPlacement() ? JSON.parse(JSON.stringify(c._editorRuntime._furniturePreviewPlacement())) : null; const committedPreviewStroke = Number(ghost()?.getAttribute('stroke-width')); @@ -266,7 +273,7 @@ const res = await page.evaluate(async () => { category('bed')?.click(); await c.updateComplete; pick('bed_double'); await c.updateComplete; ev('pointermove', stageEl(), 300, 140 + wallHalf, { pointerType: 'mouse' }); - await c.updateComplete; + await settleLive(); const thickWallPreview = c._editorRuntime?._furniturePreviewPlacement() ? JSON.parse(JSON.stringify(c._editorRuntime._furniturePreviewPlacement())) : null; ev('pointerdown', stageEl(), 300, 140 + wallHalf); @@ -290,7 +297,7 @@ const res = await page.evaluate(async () => { c.requestUpdate(); await c.updateComplete; const exteriorIntentY = 140 - wallHalf; ev('pointermove', stageEl(), 300, exteriorIntentY, { pointerType: 'mouse' }); - await c.updateComplete; + await settleLive(); const exteriorPreview = c._editorRuntime?._furniturePreviewPlacement() ? JSON.parse(JSON.stringify(c._editorRuntime._furniturePreviewPlacement())) : null; const exteriorDepth = cmToUnits(50); @@ -307,7 +314,7 @@ const res = await page.evaluate(async () => { && (exteriorChair.angle || 0) === exteriorPreview.angle; ev('pointerdown', el(exteriorChairId), 300, exteriorCentreY); ev('pointermove', stageEl(), 300, 140); - await c.updateComplete; + await settleLive(); const exteriorAxisDrag = c._decorList.find((shape) => shape.id === exteriorChairId); out.exteriorExactAxisDragPreservesSide = !!exteriorAxisDrag && near((exteriorAxisDrag.y + exteriorAxisDrag.h / 2) * 1000, exteriorCentreY, 1e-6) @@ -322,7 +329,7 @@ const res = await page.evaluate(async () => { pick('chair'); await c.updateComplete; const sharedIntentX = 550 + wallHalf / 2; ev('pointermove', stageEl(), sharedIntentX, 300, { pointerType: 'mouse' }); - await c.updateComplete; + await settleLive(); ev('pointerdown', stageEl(), sharedIntentX, 300); await c.updateComplete; const sharedChair = c._decorList.find((s) => s.symbol === 'chair' && s.id !== exteriorChairId); @@ -337,7 +344,7 @@ const res = await page.evaluate(async () => { const sharedChairY = sharedChair ? (sharedChair.y + sharedChair.h / 2) * 1000 : NaN; ev('pointerdown', el(sharedChairId), rightSurfaceCentre, sharedChairY); ev('pointermove', stageEl(), 550, sharedChairY); - await c.updateComplete; + await settleLive(); const axisDraggedChair = c._decorList.find((s) => s.id === sharedChairId); out.exactAxisDragPreservesWallSide = !!axisDraggedChair && near((axisDraggedChair.x + axisDraggedChair.w / 2) * 1000, rightSurfaceCentre, 1e-6); @@ -385,14 +392,14 @@ const res = await page.evaluate(async () => { ev('pointerdown', el(sofaId), 300, 300); out.dragStarted = !!sofaId && c._decorMove?.id === sofaId; ev('pointermove', stageEl(), 540, 300); - await c.updateComplete; + await settleLive(); const pulled = sofaNow(); out.dragMagnetTurnsItToTheWall = Math.abs(Math.abs(pulled.angle || 0) - 90) < 1e-6; out.dragMagnetPressesTheBack = near((pulled.x + pulled.w / 2) * 1000, 550 - wallHalf - cmToUnits(90) / 2, 1e-6); ev('pointermove', stageEl(), 300, 300, { shiftKey: true }); - await c.updateComplete; + await settleLive(); const shiftDragGridIndex = (sofaNow().x * 1000) / PITCH; out.shiftDragRemainsGridBound = near(shiftDragGridIndex, Math.round(shiftDragGridIndex), 1e-6); ev('pointerup', stageEl(), 300, 300, { shiftKey: true }); @@ -410,7 +417,7 @@ const res = await page.evaluate(async () => { c._cfgEpoch++; c._decorSel = sofaId; c.requestUpdate(); await c.updateComplete; await sleep(60); await c.updateComplete; - const frame = () => sr().querySelector('.dtframe'); + const frame = () => sr().querySelector('[data-hp-live-editor] .dtframe') || sr().querySelector('.dtframe'); out.frameOnSelection = !!frame(); out.furnitureFrameHasFourSideHandles = !!frame() && frame().classList.contains('dtfurnitureframe') @@ -424,9 +431,10 @@ const res = await page.evaluate(async () => { const rOf = (sel) => { const e = frame()?.querySelector(sel); return e ? +e.getAttribute('r') : NaN; }; out.handleSizeIsTheTaskOneSize = Math.abs(rOf('.dtknob') * 4 - rOf('.dthandle.dtrot')) / Math.max(rOf('.dthandle.dtrot'), 1e-9) < 0.05; - out.frameBoxIsTheShapeBox = !!c._dtBox - && near(c._dtBox.w, sofaNow().w * 1000, 1e-6) - && near(c._dtBox.h, sofaNow().h * 1000, 1e-6); + const frameBox = frame()?.querySelector('.dtbox'); + out.frameBoxIsTheShapeBox = !!frameBox + && near(+frameBox.getAttribute('width'), sofaNow().w * 1000, 1e-6) + && near(+frameBox.getAttribute('height'), sofaNow().h * 1000, 1e-6); const visiblePath = el(sofaId); const hitPath = sr().querySelector(`.dfurniturehit[data-id="${sofaId}"]`); @@ -439,7 +447,7 @@ const res = await page.evaluate(async () => { // Shift: independent, continuous width/depth + live plates. const before = { w: sofaNow().w, h: sofaNow().h }; - const b = c._dtBox || { x: 0, y: 0, w: 0, h: 0 }; + const b = box() || { x: 0, y: 0, w: 0, h: 0 }; const handles = [...(frame()?.querySelectorAll('.dthandle') || [])]; // #400: угловая ручка ищется по РОЛИ, а не по индексу в DOM. Индекс держался // на порядке отрисовки, а порядок — это решение о приоритете хита (углы @@ -451,7 +459,7 @@ const res = await page.evaluate(async () => { ev('pointerdown', se, b.x + b.w, b.y + b.h); out.cornerDragStarted = c._dtDrag?.kind === 'scale' && !!c._dtDrag?.orig; ev('pointermove', stageEl(), b.x + b.w + 100, b.y + b.h + 20, { shiftKey: true }); - await c.updateComplete; + await settleLive(); const grown = sofaNow(); out.cornerGrowsTheWidth = grown.w > before.w && near(grown.w * 1000, b.w + 100, 1e-6); out.cornerGrowsTheDepthINDEPENDENTLY = grown.h > before.h @@ -466,7 +474,7 @@ const res = await page.evaluate(async () => { && plates.some((p) => /m|м|′/.test(p.textContent)); // Without Shift the original ratio is preserved, still without grid snap. ev('pointermove', stageEl(), b.x + b.w + 101.7, b.y + b.h + 21.3); - await c.updateComplete; + await settleLive(); const proportional = sofaNow(); const wCells = (proportional.w * 1000) / PITCH; out.defaultResizePreservesRatio = near(proportional.w / proportional.h, before.w / before.h, 1e-5); @@ -478,12 +486,12 @@ const res = await page.evaluate(async () => { // A middle handle changes only one axis and keeps the opposite edge fixed. const edgeBefore = { ...sofaNow() }; - const eb = c._dtBox; + const eb = box(); const rightEdge = [...frame().querySelectorAll('.dthandle.dtedge')].find((handle) => near(+handle.getAttribute('cx'), eb.x + eb.w, 1e-6)); ev('pointerdown', rightEdge, eb.x + eb.w, eb.y + eb.h / 2); ev('pointermove', stageEl(), eb.x + eb.w + 13.7, eb.y + eb.h / 2); - await c.updateComplete; + await settleLive(); const edgeAfter = sofaNow(); out.edgeHandleChangesOneAxisContinuously = near(edgeAfter.h, edgeBefore.h, 1e-9) && near(edgeAfter.w * 1000, edgeBefore.w * 1000 + 13.7, 1e-6) @@ -494,13 +502,13 @@ const res = await page.evaluate(async () => { // Crossing the fixed corner toggles only the crossed axis. pointercancel // must restore the exact pre-gesture object and create no persisted flip. const crossBefore = JSON.parse(JSON.stringify(sofaNow())); - const cb = c._dtBox; + const cb = box(); const crossHandles = [...frame().querySelectorAll('.dthandle')] .filter((el) => !el.classList.contains('dtrot') && !el.classList.contains('dtedge')); const crossSe = crossHandles[2]; // #400: по роли, не по позиции в DOM ev('pointerdown', crossSe, cb.x + cb.w, cb.y + cb.h); ev('pointermove', stageEl(), cb.x - 25, cb.y + cb.h + 10, { shiftKey: true }); - await c.updateComplete; + await settleLive(); const crossed = sofaNow(); out.crossingTogglesOnlyHorizontalFlip = crossed.flip_h === true && !crossed.flip_v && crossed.w > 0 && crossed.h > 0 && near(crossed.x * 1000 + crossed.w * 1000, cb.x, 1e-6); @@ -517,17 +525,17 @@ const res = await page.evaluate(async () => { ev('pointerdown', frame()?.querySelector('.dthandle.dtrot'), cx + 100, cy); out.rotateStarted = c._dtDrag?.kind === 'rotate'; ev('pointermove', stageEl(), cx + 100, cy + 100); // ровно 45° - await c.updateComplete; + await settleLive(); out.rotates45 = near(sofaNow().angle || 0, 45, 1e-6); ev('pointermove', stageEl(), cx + 100, cy + 2); // ~1.1° → липнет к 0 - await c.updateComplete; + await settleLive(); const fine = sofaNow().angle; out.rotationIsFreeWithoutShift = fine > 0.5 && fine < 5; ev('pointermove', stageEl(), cx + 100, cy + 2, { shiftKey: true }); - await c.updateComplete; + await settleLive(); out.shiftSnapsRotationTo45 = (sofaNow().angle || 0) === 0; ev('pointermove', stageEl(), cx + 100, cy + 100); - await c.updateComplete; + await settleLive(); ev('pointerup', stageEl(), cx + 100, cy + 100); await c.updateComplete; out.rotationIsRendered = /rotate\(/.test(attr(sofaId, 'transform') || ''); diff --git a/demo/smoke_infinite_canvas.mjs b/demo/smoke_infinite_canvas.mjs index 6ac49765..3f414168 100644 --- a/demo/smoke_infinite_canvas.mjs +++ b/demo/smoke_infinite_canvas.mjs @@ -271,9 +271,9 @@ const farIcon = await page.evaluate(async () => { const o = {}; c._resetZoom(); await window.__waitForCamera(c); const at1 = size(); - c._applyView(4); await c.updateComplete; + c._applyView(4); await new Promise((resolve) => requestAnimationFrame(resolve)); const at4 = size(); - c._applyView(1 / 3); await c.updateComplete; + c._applyView(1 / 3); await new Promise((resolve) => requestAnimationFrame(resolve)); const atOut = size(); c._resetZoom(); await window.__waitForCamera(c); o.iconGrowsWithZoomIn = at1 > 4 && Math.abs(at4 / at1 - 4) < 0.05; diff --git a/demo/smoke_opening_measure.mjs b/demo/smoke_opening_measure.mjs index fd4d3016..415aac58 100644 --- a/demo/smoke_opening_measure.mjs +++ b/demo/smoke_opening_measure.mjs @@ -42,7 +42,10 @@ await page.evaluate(() => { return c.updateComplete && true; }); const settle = () => page.evaluate(() => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)))); -await page.waitForTimeout(220); // editor chrome transition changes stage coordinates +await page.waitForFunction(() => { + const c = window.__card; + return !!c._editorRuntime && !c._modeTransitionBusy && !c._cameraTransition?.active; +}); await settle(); const screenPt = (x, y) => page.evaluate(([x, y]) => { @@ -288,7 +291,7 @@ const previews = () => page.evaluate(() => // the click places it AT THE MAGNET and clears every hint { - const [hx, hy] = await screenPt(293.7, 141); + const [hx, hy] = await screenPt(295, 141); await page.mouse.move(hx, hy, { steps: 3 }); await settle(); await page.mouse.click(hx, hy); diff --git a/demo/smoke_pan_any_zoom.mjs b/demo/smoke_pan_any_zoom.mjs index 59c15075..8fe959c1 100644 --- a/demo/smoke_pan_any_zoom.mjs +++ b/demo/smoke_pan_any_zoom.mjs @@ -125,6 +125,7 @@ Object.assign(out, await page.evaluate(async () => { fire(h, 'pointerdown', 21, r.left + r.width / 2, r.top + r.height / 2); o.resizeHandleTakesThePointer = c._resize?.dragging === true && c._panStart === null; fire(h, 'pointermove', 21, r.left + r.width / 2 + 40, r.top + r.height / 2 + 40); + await Promise.resolve(); const v1 = { ...c._viewOr(c._baseVb()) }; o.resizeHandleDoesNotPan = Math.abs(v1.x - v0.x) < 0.5 && Math.abs(v1.y - v0.y) < 0.5; o.resizeHandleResizes = c._resize?.dragging === true && c._resize.moved === true; @@ -143,6 +144,7 @@ Object.assign(out, await page.evaluate(async () => { fire(dev, 'pointerdown', 22, r.left + r.width / 2, r.top + r.height / 2); o.deviceTakesThePointer = !!c._deviceDrag && c._panStart === null; fire(dev, 'pointermove', 22, r.left + r.width / 2 + 40, r.top + r.height / 2 + 30); + await Promise.resolve(); c._stagePointerMove({ pointerId: 22, clientX: r.left + 60, clientY: r.top + 50 }); const v1 = { ...c._viewOr(c._baseVb()) }; o.deviceDragDoesNotPan = Math.abs(v1.x - v0.x) < 0.5 && Math.abs(v1.y - v0.y) < 0.5; diff --git a/demo/smoke_partition_openings.mjs b/demo/smoke_partition_openings.mjs index c209f9b0..b8e6e26c 100644 --- a/demo/smoke_partition_openings.mjs +++ b/demo/smoke_partition_openings.mjs @@ -81,6 +81,7 @@ const out = await page.evaluate(async () => { card._opPointerMove(new PointerEvent('pointermove', { pointerId: 186, clientX: 20, clientY: 0, bubbles: true, }), saved); + await Promise.resolve(); runtime._svgPoint = originalSvgPoint; card._opPointerUp(new PointerEvent('pointerup', { pointerId: 186, bubbles: true }), saved); await update(); diff --git a/demo/smoke_render_invalidation.mjs b/demo/smoke_render_invalidation.mjs new file mode 100644 index 00000000..e3e8b966 --- /dev/null +++ b/demo/smoke_render_invalidation.mjs @@ -0,0 +1,150 @@ +// #451: production-bundle guard for the boundary between HA intake, full Lit +// renders and RAF-owned interaction layers. The large benchmark owns timings; +// this fast smoke runs on every selected-smoke pass and owns wiring semantics. +import { launch, checkAll, finish } from './serve.mjs'; + +const { page, browser } = await launch(); +const result = await page.evaluate(async () => { + const card = window.__card; + const frame = () => new Promise((done) => requestAnimationFrame(() => requestAnimationFrame(done))); + const until = async (predicate, timeout = 5000) => { + const started = performance.now(); + while (!predicate()) { + if (performance.now() - started > timeout) throw new Error('render invalidation smoke timed out'); + await new Promise((done) => setTimeout(done, 10)); + } + }; + const out = {}; + card._setMode('view', false); + await card.updateComplete; + if ('_bootSoft' in card) await until(() => card._bootSoft === false); + await frame(); + + let renders = 0; + let intakes = 0; + let scans = 0; + const originalRenderBody = card._renderBody.bind(card); + const originalObserve = card._renderLife.observe.bind(card._renderLife); + const originalBindingStatus = card._bindingStatus.bind(card); + card._renderBody = (...args) => { renders++; return originalRenderBody(...args); }; + card._renderLife.observe = (...args) => { intakes++; return originalObserve(...args); }; + card._bindingStatus = (...args) => { scans++; return originalBindingStatus(...args); }; + + const dependency = [...(card._visibleDeviceSnapshot?.entityIds || [])] + .find((id) => card.hass.states?.[id]); + const originalHass = card.hass; + const stableConfig = JSON.stringify(card._serverCfg); + const stableLayout = JSON.stringify(card._layout); + const withState = (id, state) => ({ + ...card.hass, + states: { ...card.hass.states, [id]: { ...card.hass.states[id], state } }, + }); + + const irrelevantBefore = renders; + const intakeBefore = intakes; + for (let index = 0; index < 5; index++) { + card.hass = { + ...card.hass, + states: { + ...card.hass.states, + [`sensor.hp_451_irrelevant_${index}`]: { entity_id: `sensor.hp_451_irrelevant_${index}`, state: String(index), attributes: {} }, + }, + }; + } + await card.updateComplete; + await frame(); + out.irrelevantHaHasNoFullRender = renders - irrelevantBefore === 0; + out.irrelevantHaStillIntakes = intakes - intakeBefore === 5; + + const relevantBefore = renders; + if (dependency) card.hass = withState(dependency, '451-outside'); + await card.updateComplete; + await frame(); + out.relevantHaRendersOnce = !!dependency && renders - relevantBefore === 1; + + scans = 0; + const heavy = card.renderRoot.querySelector('.wallbodies'); + const stage = card.renderRoot.querySelector('.stage'); + const rect = stage.getBoundingClientRect(); + const hoverBefore = renders; + const device = card.renderRoot.querySelector('[data-hp="device"]'); + if (device) { + device.dispatchEvent(new PointerEvent('pointerover', { + bubbles: true, composed: true, pointerId: 450, pointerType: 'mouse', isPrimary: true, + clientX: rect.left + 220, clientY: rect.top + 220, + })); + device.dispatchEvent(new PointerEvent('pointermove', { + bubbles: true, composed: true, pointerId: 450, pointerType: 'mouse', isPrimary: true, + clientX: rect.left + 225, clientY: rect.top + 225, + })); + } + await frame(); + const liveTip = card.renderRoot.querySelector('[data-hp-live-tip]'); + out.hoverUsesLightweightPaint = !!device && renders === hoverBefore + && !!liveTip && !liveTip.hidden; + device?.dispatchEvent(new PointerEvent('pointerleave', { + bubbles: false, composed: true, pointerId: 450, pointerType: 'mouse', isPrimary: true, + })); + await frame(); + + const pointer = (type, x, y, buttons) => stage.dispatchEvent(new PointerEvent(type, { + bubbles: true, composed: true, pointerId: 451, pointerType: 'mouse', isPrimary: true, + button: 0, buttons, clientX: rect.left + x, clientY: rect.top + y, + })); + pointer('pointerdown', 300, 300, 1); + await card.updateComplete; + const panBefore = renders; + const panIntakeBefore = intakes; + for (let index = 0; index < 3; index++) { + pointer('pointermove', 320 + index * 20, 315 + index * 10, 1); + if (dependency) card.hass = withState(dependency, `451-drag-${index}`); + } + await card.updateComplete; + await frame(); + out.panAndDeferredHaSkipFullRender = renders === panBefore; + out.deferredHaTicksStillIntake = intakes - panIntakeBefore === 3; + const panTerminalBefore = renders; + pointer('pointerup', 360, 335, 0); + await card.updateComplete; + await frame(); + out.panTerminalRendersOnce = renders - panTerminalBefore === 1; + out.deferredHaIsLastWins = !!dependency + && card._renderPlanHass.states?.[dependency]?.state === '451-drag-2'; + + card._setMode('plan', false); + card._tool = 'draw'; + card._path = [[100, 100]]; + card.requestUpdate(); + await card.updateComplete; + await until(() => !card._modeTransitionBusy && card._continuity.state === 'steady'); + for (let index = 0; index < 5; index++) await frame(); + await frame(); + const editorBefore = renders; + const editorPaintBefore = card._liveEditorPaintCount || 0; + for (let index = 0; index < 20; index++) { + stage.dispatchEvent(new PointerEvent('pointermove', { + bubbles: true, composed: true, pointerId: 452, pointerType: 'mouse', isPrimary: true, + clientX: rect.left + 110 + index, clientY: rect.top + 120 + index, + })); + } + await frame(); + out.editorMovesSkipFullRender = renders === editorBefore; + out.editorMovesUseRafPaint = (card._liveEditorPaintCount || 0) > editorPaintBefore; + card._cursorPt = null; + card._path = []; + const editorTerminalBefore = renders; + card.requestUpdate(); + await card.updateComplete; + await frame(); + out.editorTerminalHasAtMostOneRender = renders - editorTerminalBefore <= 1; + + out.heavySceneIdentityStable = card.renderRoot.querySelector('.wallbodies') === heavy; + out.pointerWindowsSkipDiagnosticScans = scans === 0; + out.interactionsDoNotWriteConfig = JSON.stringify(card._serverCfg) === stableConfig; + out.interactionsDoNotWriteLayout = JSON.stringify(card._layout) === stableLayout; + card.hass = originalHass; + return out; +}); + +checkAll(result); +await finish(browser, result); diff --git a/demo/smoke_resize_pointer_real_plan.mjs b/demo/smoke_resize_pointer_real_plan.mjs index 4a76450e..633d1c38 100644 --- a/demo/smoke_resize_pointer_real_plan.mjs +++ b/demo/smoke_resize_pointer_real_plan.mjs @@ -114,10 +114,13 @@ const sharedX = () => page.evaluate(() => { ))).map((point) => point[0]); }); const domHasSharedX = (x) => page.evaluate((wanted) => - [...window.__card.renderRoot.querySelectorAll('.rszhandle[aria-disabled="false"]')] + [...(window.__card.renderRoot.querySelector('[data-hp-live-editor] .hp-live-resize') + || window.__card.renderRoot).querySelectorAll('.rszhandle[aria-disabled="false"]')] .some((entry) => Math.abs(Number(entry.getAttribute('cx')) - wanted) < 1), x); const sharedHandleX = () => page.evaluate(() => { - const candidates = [...window.__card.renderRoot.querySelectorAll( + const activeRoot = window.__card.renderRoot.querySelector('[data-hp-live-editor] .hp-live-resize') + || window.__card.renderRoot; + const candidates = [...activeRoot.querySelectorAll( '.rszhandle[aria-disabled="false"]', )].filter((entry) => Math.abs(Number(entry.getAttribute('cy')) - 529.166667) < 2); return candidates diff --git a/demo/smoke_room_fit.mjs b/demo/smoke_room_fit.mjs index 27e6bff5..099bebd5 100644 --- a/demo/smoke_room_fit.mjs +++ b/demo/smoke_room_fit.mjs @@ -80,11 +80,16 @@ const out = await page.evaluate(async () => { const expectedLeft = ((position[0] - activeView.x) / activeView.w) * 100; const expectedTop = ((position[1] - activeView.y) / activeView.h) * 100; const actualView = activeSvg?.getAttribute('viewBox')?.trim().split(/\s+/).map(Number) || []; + const labelRect = activeLabel?.getBoundingClientRect(); + const stageRect = root.querySelector('.stage')?.getBoundingClientRect(); syncFrames.push(actualView.length === 4 && actualView.every((value, index) => Math.abs(value - [activeView.x, activeView.y, activeView.w, activeView.h][index]) < 1e-6) - && Math.abs(Number.parseFloat(activeLabel.style.left) - expectedLeft) < 1e-3 - && Math.abs(Number.parseFloat(activeLabel.style.top) - expectedTop) < 1e-3); + && !!labelRect && !!stageRect + && Math.abs(labelRect.left + labelRect.width / 2 + - (stageRect.left + stageRect.width * expectedLeft / 100)) < 1 + && Math.abs(labelRect.top + labelRect.height / 2 + - (stageRect.top + stageRect.height * expectedTop / 100)) < 1); } result.svgAndHtmlShareTweenFrames = syncFrames.length > 0 && syncFrames.every(Boolean); await waitCamera(); diff --git a/demo/smoke_room_tooltip_toggle.mjs b/demo/smoke_room_tooltip_toggle.mjs index 1f0d0439..d7949383 100644 --- a/demo/smoke_room_tooltip_toggle.mjs +++ b/demo/smoke_room_tooltip_toggle.mjs @@ -94,7 +94,7 @@ const result = await page.evaluate(async () => { await card._saveSettingsDialog(); await card.updateComplete; out.falsePersists = card._serverCfg.settings.show_room_tooltip === false; - out.visibleRoomTipCleared = card._tip === null && !root().querySelector('.tip'); + out.visibleRoomTipCleared = card._tip === null && root().querySelector('.tip')?.hidden === true; let roomAreaCalls = 0; const originalRoomArea = card._roomArea.bind(card); @@ -105,7 +105,7 @@ const result = await page.evaluate(async () => { room().dispatchEvent(mouse('pointerenter', 190, 190)); room().dispatchEvent(mouse('pointermove', 190, 190)); await card.updateComplete; - out.disabledRoomTip = card._tip === null && !root().querySelector('.tip'); + out.disabledRoomTip = card._tip === null && root().querySelector('.tip')?.hidden === true; out.disabledSkipsArea = roomAreaCalls === 0; out.roomHighlightSurvives = card._hoverRoom !== null && !!root().querySelector('.room-hover-fill-layer,.room-hover-outline-layer'); diff --git a/demo/smoke_ux_fixes.mjs b/demo/smoke_ux_fixes.mjs index a303b611..d87819cd 100644 --- a/demo/smoke_ux_fixes.mjs +++ b/demo/smoke_ux_fixes.mjs @@ -54,7 +54,8 @@ const res = await page.evaluate(async () => { const humLine = metricLines.findIndex((line) => line.includes('средняя влажность')); const lqiLine = metricLines.findIndex((line) => line.includes(c._t('tip.lqi'))); out.tipMetricOrder = tempLine >= 0 && humLine === tempLine + 1 && lqiLine === humLine + 1; - out.tipPositionUnchanged = sr().querySelector('.tip')?.getAttribute('style') === 'left:212px;top:212px'; + out.tipPositionUnchanged = sr().querySelector('.tip')?.style.left === '212px' + && sr().querySelector('.tip')?.style.top === '212px'; // _roomArea consumes the rendered room geometry (0..1000), not the stored // normalized config polygon (0..1). const expectedArea = c._roomArea(c._spaceModel().rooms[0]); diff --git a/dist/houseplan-assets.json b/dist/houseplan-assets.json index 99bc1ffe..b344cb35 100644 --- a/dist/houseplan-assets.json +++ b/dist/houseplan-assets.json @@ -1,169 +1,182 @@ { "schema": 1, - "fingerprint": "7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91", + "fingerprint": "71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7", "entry": "houseplan-card.js", "initialViewFiles": [ - "houseplan-assets/houseplan-card-DcH_msOh.js", + "houseplan-assets/houseplan-card-Csq_DR2Y.js", "houseplan-card.js" ], - "initialViewGzipBytes": 295744, + "initialViewGzipBytes": 296722, "lazyFiles": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/de-CcfVGZcf.js", - "houseplan-assets/editor-CrMXGx3s.js", - "houseplan-assets/fr-OCiOh_q7.js", - "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js", - "houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js", - "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/de-DR62x0IQ.js", + "houseplan-assets/editor-BVOuJXsz.js", + "houseplan-assets/fr-D4YIJQhT.js", + "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js", + "houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js", + "houseplan-assets/live-interaction-runtime-C2JvTP_O.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js", + "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js" ], - "lazyGzipBytes": 234162, + "lazyGzipBytes": 240676, "lazyEditorFiles": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/editor-CrMXGx3s.js", - "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/editor-BVOuJXsz.js", + "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js" ], - "lazyEditorGzipBytes": 173958, + "lazyEditorGzipBytes": 178334, "lazyOnboardingFiles": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js" ], - "lazyOnboardingGzipBytes": 14235, + "lazyOnboardingGzipBytes": 14241, "lazyLocaleFiles": [ - "houseplan-assets/de-CcfVGZcf.js", - "houseplan-assets/fr-OCiOh_q7.js" + "houseplan-assets/de-DR62x0IQ.js", + "houseplan-assets/fr-D4YIJQhT.js" ], - "lazyLocaleGzipBytes": 49109, + "lazyLocaleGzipBytes": 49115, "files": [ { - "path": "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "sha256": "ede625d42274e8806716776cb0e4de9707fa6713bd990f13c564fc9348fe08c5", - "rawBytes": 21418, + "path": "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "sha256": "709ac3421f7630a539c5c1c27248b39f76e6d9d2b54b75057abb0b4af70bc844", + "rawBytes": 21417, "gzipBytes": 7282, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/de-CcfVGZcf.js", - "sha256": "ceca4bfbade91448d50b8ce35e0e894fcf58023f692d6ecc11968add4a501661", + "path": "houseplan-assets/de-DR62x0IQ.js", + "sha256": "0377277d00b6e025e587aa7404a3cecea12c56d873fa9741680a4ab246914d09", "rawBytes": 82182, - "gzipBytes": 24824, + "gzipBytes": 24827, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/editor-CrMXGx3s.js", - "sha256": "70f2f6ee3de165adf92a553886464362fdfcad7dc99e294340e945d82369ece9", - "rawBytes": 3826, - "gzipBytes": 1579, + "path": "houseplan-assets/editor-BVOuJXsz.js", + "sha256": "efde2f9d2f8bc1f5fb961bb3c57feb8a291018f2871c4c068ab60bae58106446", + "rawBytes": 3831, + "gzipBytes": 1583, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/fr-OCiOh_q7.js", - "sha256": "35feec4d0e40f8d157537b89c32e7cf066fdb984c020c2112303b6863bfa2bfd", + "path": "houseplan-assets/fr-D4YIJQhT.js", + "sha256": "e5c0ff025e2826a8a43b53e6af22c7314a5b72effd2b41d7cfd38a128f998925", "rawBytes": 84312, - "gzipBytes": 24285, + "gzipBytes": 24288, "isEntry": false, "imports": [], "dynamicImports": [] }, { - "path": "houseplan-assets/houseplan-card-DcH_msOh.js", - "sha256": "3ad91eaf921e0d682e4b9c14af09d754d1511b432624cc5e7064d71b00aabe0e", - "rawBytes": 1050357, - "gzipBytes": 294949, + "path": "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "sha256": "2751d48b52d10030532b2de87f0f0c7f38c283223bae57c8728b2717b115c353", + "rawBytes": 1053904, + "gzipBytes": 295925, "isEntry": false, "imports": [], "dynamicImports": [ - "houseplan-assets/de-CcfVGZcf.js", - "houseplan-assets/editor-CrMXGx3s.js", - "houseplan-assets/fr-OCiOh_q7.js", - "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js", - "houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js" + "houseplan-assets/de-DR62x0IQ.js", + "houseplan-assets/editor-BVOuJXsz.js", + "houseplan-assets/fr-D4YIJQhT.js", + "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js", + "houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js", + "houseplan-assets/live-interaction-runtime-C2JvTP_O.js" ] }, { - "path": "houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js", - "sha256": "9045a38e1be7f4a93f6934c82380b94e2e9df95532bcfd2085d25c5225a00a04", - "rawBytes": 608951, - "gzipBytes": 160252, + "path": "houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js", + "sha256": "db3ca36d567fb682fb633555bdfa963db0d2f5e9682c1532bdf6ef81c17591fc", + "rawBytes": 622491, + "gzipBytes": 164622, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/houseplan-card-DcH_msOh.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js" ], "dynamicImports": [ - "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js" + "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js" ] }, { - "path": "houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js", - "sha256": "1619296b625e2dd17d93ab4bc01e928dec730ddf5b8a56e12a7bc276addf10e6", + "path": "houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js", + "sha256": "bb156e1a1244328a728875141ddf47d2ebe0d1e12e763eb1336e2f72837bcfaf", "rawBytes": 28088, - "gzipBytes": 6953, + "gzipBytes": 6959, "isEntry": false, "imports": [ - "houseplan-assets/backdrop-pick-CXzP7hUG.js", - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/backdrop-pick-CEZBr4qx.js", + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js", - "sha256": "4e87b71be6179c03adb570887921e1a86ba8965483be24135f3bce2d27a5ed82", - "rawBytes": 6007, - "gzipBytes": 2380, + "path": "houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js", + "sha256": "aac582f8d2e0586aeed749ff9581f1fb7d11bb15493cc939bd37b61f903c05aa", + "rawBytes": 6006, + "gzipBytes": 2376, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js", - "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js", + "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/zigbee-topology-runtime-BFjOXm55.js", - "sha256": "dfd3a3dfc807b9035019ea89187327f20975ad11ff0635f1c9df4ad542d3a6f6", - "rawBytes": 3941, - "gzipBytes": 1762, + "path": "houseplan-assets/live-interaction-runtime-C2JvTP_O.js", + "sha256": "91e8a03ebb3fb35b89c974e2bcf3ee1eff8523f9d9cb1ac93a061c3f751706d1", + "rawBytes": 5196, + "gzipBytes": 2128, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js", - "houseplan-assets/zigbee-topology-x104QxRs.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] }, { - "path": "houseplan-assets/zigbee-topology-x104QxRs.js", - "sha256": "d52ca29eeb5a62e0a00362844d00c66258fef6c375e090b7173515eb78ccf856", + "path": "houseplan-assets/zigbee-topology-D9L1ESh4.js", + "sha256": "817e5b42008db12cdf922275098eb06a536408dbd01f464859dccee434c2451b", "rawBytes": 12382, - "gzipBytes": 4845, + "gzipBytes": 4847, "isEntry": false, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" + ], + "dynamicImports": [] + }, + { + "path": "houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js", + "sha256": "8197d48ba2f6eb615024f0cabca6ff12c10a3442fa19ebcd536bd5d3d5586c34", + "rawBytes": 3941, + "gzipBytes": 1764, + "isEntry": false, + "imports": [ + "houseplan-assets/houseplan-card-Csq_DR2Y.js", + "houseplan-assets/zigbee-topology-D9L1ESh4.js" ], "dynamicImports": [] }, { "path": "houseplan-card.js", - "sha256": "e50d3f3c6e84d612ead3f39c6f39d987697dbea608e85d839a41848268cb73a4", + "sha256": "dbba45693954cf08e2165d88bdcb9e5c295d3782fe5fae9943e079e22135e22c", "rawBytes": 1183, - "gzipBytes": 795, + "gzipBytes": 797, "isEntry": true, "imports": [ - "houseplan-assets/houseplan-card-DcH_msOh.js" + "houseplan-assets/houseplan-card-Csq_DR2Y.js" ], "dynamicImports": [] } diff --git a/dist/houseplan-assets/backdrop-pick-CXzP7hUG.js b/dist/houseplan-assets/backdrop-pick-CEZBr4qx.js similarity index 59% rename from dist/houseplan-assets/backdrop-pick-CXzP7hUG.js rename to dist/houseplan-assets/backdrop-pick-CEZBr4qx.js index ca0aea1a..3e82be4e 100644 --- a/dist/houseplan-assets/backdrop-pick-CXzP7hUG.js +++ b/dist/houseplan-assets/backdrop-pick-CEZBr4qx.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{dn as e,c as t,A as i,j as o,k as n,dp as r}from"./houseplan-card-DcH_msOh.js";function s(e){const t=e.visualViewport;return t&&Number.isFinite(t.width)&&Number.isFinite(t.height)?{left:Number(t.offsetLeft)||0,top:Number(t.offsetTop)||0,width:Math.max(0,Number(t.width)||0),height:Math.max(0,Number(t.height)||0)}:{left:0,top:0,width:e.innerWidth,height:e.innerHeight}}function a(e,t,i,o=7,n=8){const r=i.left+n,s=i.top+n,a=Math.max(r,i.left+i.width-n),l=Math.max(s,i.top+i.height-n),h=Math.max(0,a-r),c=Math.max(0,l-s),d=Math.min(Math.max(0,t.width),h),u=Math.min(Math.max(0,t.height),c);let p=e.left;p+d>a&&(p=e.right-d),p=Math.min(Math.max(r,p),Math.max(r,a-d));const g=e.bottom+o,_=e.top-o-u;let m="bottom",f=g;return g+u>l&&_>=s?(m="top",f=Math.min(Math.max(s,_),Math.max(s,l-u))):f=Math.min(Math.max(s,g),Math.max(s,l-u)),{left:Math.round(p),top:Math.round(f),side:m,maxWidth:Math.round(h),maxHeight:Math.round(c)}}class l{constructor(e,t,i){this.owner=e,this.name=t,this.keydown=i,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(e=!1){const t=this.window();return!e&&!!t&&function(e){const t=e.HTMLElement?.prototype;return"function"==typeof t?.showPopover&&"function"==typeof t?.hidePopover}(t)}get hasFallback(){return!!this.host}containsPath(e){return e.includes(this.owner)||!!this.host&&e.includes(this.host)}ownsActiveElement(){let e=this.owner.ownerDocument.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return!(!e||e!==this.owner&&!this.owner.shadowRoot?.contains(e)&&!this.root?.contains(e))}renderFallback(i,o){const n=this.ensureFallback();return n?(e(t`${i}`,n),n):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&e(i,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const e=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!e)return null;const t=this.owner.ownerDocument.createElement("div");return t.dataset.hpOverlay=this.name,t.style.display="contents",this.keydown&&t.addEventListener("keydown",this.keydown,!0),e.append(t),this.host=t,this.root=t.attachShadow({mode:"open"}),this.root}}let h=0;class c extends n{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++h,this._surfacePointerEnter=()=>{const e=this._window();this._closeTimer&&e?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closeHelp(!1,"outside")},this._dialogScroll=e=>{const t=this._floating.containsPath(e.composedPath()),i=e.target,o=this._dialog(),n=i instanceof Node&&(i===o||i instanceof Element&&i.contains(this));var r;this._open&&(r=n,!t&&r)&&this._closeHelp(!1,"scroll")},this._keyDown=e=>{if(!this._open||e.defaultPrevented)return;if("Escape"===e.key)return e.preventDefault(),e.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const t=this.renderRoot.querySelector(".trigger");if(!t||!e.composedPath().includes(t))return;const i=this._surface();if(!i||i.scrollHeight<=i.clientHeight)return;const o=Math.max(40,.8*i.clientHeight),n={ArrowDown:40,ArrowUp:-40,PageDown:o,PageUp:-o};if("Home"===e.key)i.scrollTop=0;else if("End"===e.key)i.scrollTop=i.scrollHeight;else{if(!(e.key in n))return;i.scrollBy({top:n[e.key],behavior:"auto"})}e.preventDefault(),e.stopImmediatePropagation()},this._floating=new l(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const e=this._window();e&&(this._positionRaf=e.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const e=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&e?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(e){if(e.has("pointerHover")&&!this.pointerHover){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&e.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return e=this.text,t=this.ariaLabel,"string"==typeof e&&e.trim().length>0&&"string"==typeof t&&t.trim().length>0;var e,t}_clearTimers(){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._closeTimer&&e?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const e=this._window();this.pointerHover&&e&&!this._open&&!this._openTimer&&(this._closeTimer&&e.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=e.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const e=this._window(),t=this.renderRoot.querySelector(".trigger");e&&!t?.matches(":focus-visible")&&(this._openTimer&&e.clearTimeout(this._openTimer),this._closeTimer&&e.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=e.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(e){this.pointerHover&&"mouse"===e.pointerType&&this._scheduleOpen()}_triggerPointerLeave(e){"mouse"===e.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const e=this.renderRoot.querySelector(".trigger");e?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const e=this._window();e?.addEventListener("resize",this._queuePosition),e?.addEventListener("orientationchange",this._queuePosition),e?.visualViewport?.addEventListener("resize",this._queuePosition),e?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const e=this._window();e?.removeEventListener("resize",this._queuePosition),e?.removeEventListener("orientationchange",this._queuePosition),e?.visualViewport?.removeEventListener("resize",this._queuePosition),e?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:e=>this._closeHelp("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closeHelp()))}_closeHelp(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const i=this._overlayDispose;this._overlayDispose=null,i?.();const o=this.renderRoot.querySelector(".tooltip");if(o?.hidePopover)try{o.matches(":popover-open")&&o.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(e){return t`
    a&&(p=e.right-d),p=Math.min(Math.max(r,p),Math.max(r,a-d));const g=e.bottom+o,_=e.top-o-u;let m="bottom",f=g;return g+u>l&&_>=s?(m="top",f=Math.min(Math.max(s,_),Math.max(s,l-u))):f=Math.min(Math.max(s,g),Math.max(s,l-u)),{left:Math.round(p),top:Math.round(f),side:m,maxWidth:Math.round(h),maxHeight:Math.round(c)}}class l{constructor(e,t,i){this.owner=e,this.name=t,this.keydown=i,this.host=null,this.root=null}window(){return this.owner.ownerDocument.defaultView}dialog(){return this.owner.closest("hp-dialog")}usesPopover(e=!1){const t=this.window();return!e&&!!t&&function(e){const t=e.HTMLElement?.prototype;return"function"==typeof t?.showPopover&&"function"==typeof t?.hidePopover}(t)}get hasFallback(){return!!this.host}containsPath(e){return e.includes(this.owner)||!!this.host&&e.includes(this.host)}ownsActiveElement(){let e=this.owner.ownerDocument.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return!(!e||e!==this.owner&&!this.owner.shadowRoot?.contains(e)&&!this.root?.contains(e))}renderFallback(i,o){const n=this.ensureFallback();return n?(e(t`${i}`,n),n):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&e(i,this.root),this.host?.remove(),this.host=null,this.root=null}ensureFallback(){if(this.root?.isConnected)return this.root;const e=this.dialog()?.overlayPortal()||this.owner.ownerDocument.body;if(!e)return null;const t=this.owner.ownerDocument.createElement("div");return t.dataset.hpOverlay=this.name,t.style.display="contents",this.keydown&&t.addEventListener("keydown",this.keydown,!0),e.append(t),this.host=t,this.root=t.attachShadow({mode:"open"}),this.root}}let h=0;class c extends n{constructor(){super(...arguments),this.text="",this.ariaLabel="",this.pointerHover=!1,this._open=!1,this._openTimer=0,this._closeTimer=0,this._positionRaf=0,this._forceFallback=!1,this._hoverOwned=!1,this._overlayDispose=null,this._scrollDialog=null,this._descriptionId="hp-help-description-"+ ++h,this._surfacePointerEnter=()=>{const e=this._window();this._closeTimer&&e?.clearTimeout(this._closeTimer),this._closeTimer=0},this._surfacePointerLeave=()=>{this._scheduleClose()},this._outsidePointerDown=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closeHelp(!1,"outside")},this._dialogScroll=e=>{const t=this._floating.containsPath(e.composedPath()),i=e.target,o=this._dialog(),n=i instanceof Node&&(i===o||i instanceof Element&&i.contains(this));var r;this._open&&(r=n,!t&&r)&&this._closeHelp(!1,"scroll")},this._keyDown=e=>{if(!this._open||e.defaultPrevented)return;if("Escape"===e.key)return e.preventDefault(),e.stopImmediatePropagation(),void this._closeHelp(this._floating.ownsActiveElement(),"escape");const t=this.renderRoot.querySelector(".trigger");if(!t||!e.composedPath().includes(t))return;const i=this._surface();if(!i||i.scrollHeight<=i.clientHeight)return;const o=Math.max(40,.8*i.clientHeight),n={ArrowDown:40,ArrowUp:-40,PageDown:o,PageUp:-o};if("Home"===e.key)i.scrollTop=0;else if("End"===e.key)i.scrollTop=i.scrollHeight;else{if(!(e.key in n))return;i.scrollBy({top:n[e.key],behavior:"auto"})}e.preventDefault(),e.stopImmediatePropagation()},this._floating=new l(this,"help",this._keyDown),this._queuePosition=()=>{if(!this._open||this._positionRaf)return;const e=this._window();e&&(this._positionRaf=e.requestAnimationFrame(()=>{this._positionRaf=0,this._position()||this._closeHelp()}))}}connectedCallback(){super.connectedCallback(),this.addEventListener("keydown",this._keyDown,!0)}willUpdate(){this.toggleAttribute("data-has-content",this._hasContent())}disconnectedCallback(){const e=this.ownerDocument.defaultView;this._unsubscribeOpenListeners(),this.removeEventListener("keydown",this._keyDown,!0),this._clearTimers(),this._positionRaf&&e?.cancelAnimationFrame(this._positionRaf),this._positionRaf=0,this._closeHelp(!1,"disconnect"),super.disconnectedCallback()}updated(e){if(e.has("pointerHover")&&!this.pointerHover){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._openTimer=0,this._hoverOwned&&this._closeHelp(!1,"outside")}this._hasContent()||!this._open?this._open&&e.has("text")&&(this._usesPopover()||this._renderFallback(),this._queuePosition()):this._closeHelp()}_window(){return this._floating.window()}_dialog(){return this._floating.dialog()}_usesPopover(){return this._floating.usesPopover(this._forceFallback)}_hasContent(){return e=this.text,t=this.ariaLabel,"string"==typeof e&&e.trim().length>0&&"string"==typeof t&&t.trim().length>0;var e,t}_clearTimers(){const e=this._window();this._openTimer&&e?.clearTimeout(this._openTimer),this._closeTimer&&e?.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=0}_scheduleOpen(){const e=this._window();this.pointerHover&&e&&!this._open&&!this._openTimer&&(this._closeTimer&&e.clearTimeout(this._closeTimer),this._closeTimer=0,this._openTimer=e.setTimeout(()=>{this._openTimer=0,this.isConnected&&(this._hoverOwned=!0,this._openHelp())},300))}_scheduleClose(){const e=this._window(),t=this.renderRoot.querySelector(".trigger");e&&!t?.matches(":focus-visible")&&(this._openTimer&&e.clearTimeout(this._openTimer),this._closeTimer&&e.clearTimeout(this._closeTimer),this._openTimer=0,this._closeTimer=e.setTimeout(()=>{this._closeTimer=0,this.isConnected&&this._closeHelp()},150))}_triggerPointerEnter(e){this.pointerHover&&"mouse"===e.pointerType&&this._scheduleOpen()}_triggerPointerLeave(e){"mouse"===e.pointerType&&this._scheduleClose()}_triggerFocus(){queueMicrotask(()=>{const e=this.renderRoot.querySelector(".trigger");e?.matches(":focus-visible")&&(this._hoverOwned=!1,this._openHelp())})}_triggerBlur(){this._scheduleClose()}_triggerClick(){this._open?this._closeHelp():(this._hoverOwned=!1,this._openHelp())}_subscribeOpenListeners(){this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("keydown",this._keyDown,!0);const e=this._window();e?.addEventListener("resize",this._queuePosition),e?.addEventListener("orientationchange",this._queuePosition),e?.visualViewport?.addEventListener("resize",this._queuePosition),e?.visualViewport?.addEventListener("scroll",this._queuePosition),this._scrollDialog=this._dialog(),this._scrollDialog?.addEventListener("scroll",this._dialogScroll,!0)}_unsubscribeOpenListeners(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("keydown",this._keyDown,!0);const e=this._window();e?.removeEventListener("resize",this._queuePosition),e?.removeEventListener("orientationchange",this._queuePosition),e?.visualViewport?.removeEventListener("resize",this._queuePosition),e?.visualViewport?.removeEventListener("scroll",this._queuePosition),this._scrollDialog?.removeEventListener("scroll",this._dialogScroll,!0),this._scrollDialog=null}async _openHelp(){!this._open&&this._hasContent()&&(this._clearTimers(),this._forceFallback=!1,this._open=!0,this._subscribeOpenListeners(),await this.updateComplete,this._open&&(this._usesPopover()||this._renderFallback(),this._position()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:e=>this._closeHelp("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closeHelp()))}_closeHelp(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;this._clearTimers();const i=this._overlayDispose;this._overlayDispose=null,i?.();const o=this.renderRoot.querySelector(".tooltip");if(o?.hidePopover)try{o.matches(":popover-open")&&o.hidePopover()}catch{}this._open=!1,this._hoverOwned=!1,this._unsubscribeOpenListeners(),this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_tooltipTemplate(e){return t``}_renderFallback(){const e=c.styles.cssText;this._floating.renderFallback(this._tooltipTemplate(!1),e)}_surface(){return this._floating.surface(".tooltip",this._usesPopover())}_position(){if(!this._open)return!1;const e=this._window(),t=this.renderRoot.querySelector(".trigger");let i=this._surface();if(!e||!t?.isConnected||!i?.isConnected)return!1;const o=s(e);if(i.style.maxWidth=`${Math.max(0,Math.min(320,o.width-16))}px`,i.style.maxHeight=`${Math.max(0,o.height-16)}px`,i.style.visibility="hidden",this._usesPopover()&&i.showPopover)try{i.matches(":popover-open")||i.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),i=this._surface(),!i?.isConnected)return!1;i.style.visibility="hidden"}const n=t.getBoundingClientRect(),r=i.getBoundingClientRect();if(!(n.width&&n.height&&r.width&&r.height))return!1;const l=a(n,r,o);return i.style.left=`${l.left}px`,i.style.top=`${l.top}px`,i.dataset.side=l.side,i.style.visibility="",!0}render(){if(!this._hasContent())return i;const e=this.ariaLabel.trim();return t` ({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`h[e.name]||e.name} + @value-changed=${this._valueChanged} + >`;return c` + ${d(f.slice(0,3))} + ${_?c``:a} + ${d(f.slice(3))} + `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h); diff --git a/dist/houseplan-assets/editor-CrMXGx3s.js b/dist/houseplan-assets/editor-CrMXGx3s.js deleted file mode 100644 index 94d7ad4c..00000000 --- a/dist/houseplan-assets/editor-CrMXGx3s.js +++ /dev/null @@ -1,14 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{k as e,l as o,t,dq as s,A as a,dr as i,ds as l,dt as r,E as n,c}from"./houseplan-card-DcH_msOh.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const r="string"==typeof this._config?.default_floor?this._config.default_floor:"",n=[...e];return r&&!e.some(e=>e.value===r)&&n.unshift({value:r,label:r}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:n}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return r();if("warm"===e)return n;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`h[e.name]||e.name} - @value-changed=${this._valueChanged} - >`;return c` - ${d(f.slice(0,3))} - ${_?c``:a} - ${d(f.slice(3))} - `}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h); diff --git a/custom_components/houseplan/frontend/houseplan-assets/fr-OCiOh_q7.js b/dist/houseplan-assets/fr-D4YIJQhT.js similarity index 99% rename from custom_components/houseplan/frontend/houseplan-assets/fr-OCiOh_q7.js rename to dist/houseplan-assets/fr-D4YIJQhT.js index 3ed3c3d2..b2f635dd 100644 --- a/custom_components/houseplan/frontend/houseplan-assets/fr-OCiOh_q7.js +++ b/dist/houseplan-assets/fr-D4YIJQhT.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft_title":"Supprimer le contour inachevé ?","confirm.delete_draft_body":"Tout le contour de pièce inachevé sera supprimé. L’annulation ne modifie rien.","confirm.delete_draft_segment_title":"Supprimer le segment de contour ?","confirm.delete_draft_segment_body":"Ce segment de brouillon sera supprimé. Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","history.device_move":"Déplacer {name}","history.device_stale":"L’historique des positions n’est plus applicable et a été effacé","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker_title":"Supprimer l’appareil du plan ?","confirm.remove_marker_body":"L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space_title":"Supprimer l’espace ?","confirm.delete_space_body":"L’espace, toutes ses pièces et tout le tracé du plan seront supprimés.","confirm.delete_space_vac_routes":"Les affectations de cartes de robots seront aussi supprimées : {count}.","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.cell_cm.help":"Relie la grille aux dimensions réelles : les longueurs, les surfaces, l’épaisseur des murs, la taille des ouvertures et le rayon du halo en dépendent. Une modification ultérieure change les dimensions calculées de tout l’espace sans déplacer ses points sur le plan.","space.cell_cm.help.aria":"Aide : échelle de l’espace","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_style.help":"Les murs d’épaisseur nulle en pointillés laissent passer le halo et les rayons du soleil, tandis que les murs pleins les bloquent comme une barrière sans surface. Ce choix s’applique à tous ces murs de l’espace, même si leur ligne est masquée en mode Vue.","space.zero_wall_style.help.aria":"Aide : style des murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","space.fill_mode.help":"« Couleur personnalisée » définit le style, tandis que « Lumière », « Température » et « LQI » utilisent les données actuelles de Home Assistant. Le halo s’active séparément.","space.fill_mode.help.aria":"Aide : mode de remplissage des pièces","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.show_hidden.help":"Affiche temporairement les marqueurs masqués et désactivés sous forme de fantômes réservés à l’éditeur. La visibilité enregistrée des appareils ne change pas.","device_inbox.show_hidden.help.aria":"Aide : afficher les appareils masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration « {integration} » exclue par les filtres de découverte","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","device_inbox.filters_excluded":"Intégrations exclues","device_inbox.filters_group_lights":"Regrouper les lumières de la pièce en un marqueur","device_inbox.filters_preview_appear":"apparaîtra : {count}","device_inbox.filters_preview_hide":"disparaîtra : {count}","device_inbox.filters_preview_lights":"lumières concernées : {count}","device_inbox.filters_reset":"Rétablir les recommandées","device_inbox.filters_save":"Enregistrer les filtres","device_inbox.filters_search_ph":"Ajouter une intégration…","device_inbox.filters_title":"Filtres de découverte","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.flip_h":"Retourner horizontalement","decor.flip_v":"Retourner verticalement","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.glow_radius.help":"Définit le rayon général du halo en mètres ou en pieds ; le rayon propre à un appareil remplace cette valeur.","gs.glow_radius.help.aria":"Aide : rayon général du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls.help":"Avec l’action « Basculer l’état », les sources répertoriées basculent avec ce marqueur. Leur halo reste sur leurs propres marqueurs et ce lien ne transforme pas à lui seul le marqueur de contrôle en source lumineuse.","marker.controls.help.aria":"Aide : contrôler d’autres sources lumineuses","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","room.fit_action":"Ajuster la pièce {name}","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.value_source":"Source de la valeur","marker.value_source.help":"Choisissez la valeur qui remplace l’icône. L’action au toucher ne change pas.","marker.value_source.help.aria":"Aide : source de la valeur dans le marqueur","marker.value_source_auto":"Automatique (comme avant)","marker.value_source_missing_hint":"La source enregistrée est indisponible. Le marqueur affiche — jusqu’à son retour ou jusqu’au choix d’une autre valeur.","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"« Auto » utilise le rôle déterminé de l’appareil lié, « Toujours » force sa propre source et « Jamais » l’exclut ; les lumières liées ci-dessus restent indépendantes.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock_title":"Déverrouiller ?","confirm.unlock_body":"House Plan enverra une commande de déverrouillage à cette serrure.","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan_title":"Supprimer le fichier du plan ?","confirm.delete_plan_body":"Le fichier sera supprimé du serveur. Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","tap.none":"Ne rien faire","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.route_warn_unmapped":"Le robot nettoie une carte qui n'est assignée à aucun étage","vac.route_warn_needs_calibration":"La carte est assignée à un étage mais n'est pas calibrée","vac.route_warn_ambiguous":"Deux cartes sont indiscernables — aucun étage choisi","vac.route_warn_missing_space":"L'espace de cette carte a été supprimé","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_mode.help":"« Suit le soleil » utilise sun.sun ou, à défaut, l’heure locale du navigateur ; « Couleur statique » affiche toujours la couleur choisie.","gs.bg_mode.help.aria":"Aide : arrière-plan général du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north.help":"L’angle se mesure dans le sens horaire depuis la verticale supérieure du plan ; le nord sert aux rayons des fenêtres, pas à l’arrière-plan « Suit le soleil ».","gs.north.help.aria":"Aide : direction générale du nord","gs.north_ph":"non défini","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","support.title":"Aide et commentaires","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.bg_mode.help":"Héritez de l’arrière-plan général ou remplacez-le pour cet espace par une couleur statique ou le mode « Suit le soleil ».","space.bg_mode.help.aria":"Aide : arrière-plan de l’espace","space.north":"Nord sur le plan (remplacement)","space.north.help":"Remplace le nord général pour cet espace ; l’angle se mesure dans le sens horaire depuis la verticale supérieure du plan et sert aux rayons des fenêtres.","space.north.help.aria":"Aide : nord de cet espace","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","decor.image":"Image","decor.image_title":"Images personnalisées","decor.image_upload":"Importer une image","decor.image_replace":"Remplacer l’image","decor.image_uploading":"Importation…","decor.image_asset":"Fichier image","decor.image_pick_hint":"Importez une image ou choisissez-en une existante.","decor.image_place_hint":"Cliquez sur le plan pour placer l’image.","decor.image_none":"Aucune image importée pour le moment.","decor.image_unavailable":"Image indisponible","decor.image_used":"Utilisée dans {n} objets","decor.image_upload_failed":"Impossible d’importer l’image : {err}","decor.image_error_capacity":"Le stockage des images personnalisées est plein.","decor.image_in_use":"Supprimez toutes les copies placées avant ce fichier.","decor.image_delete_title":"Supprimer le fichier image ?","decor.image_delete_message":"Supprimer « {name} » de House Plan ? Cette action est irréversible.","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.support_invalid_message":"Vérifiez le message d’assistance et réessayez.","backup.error.support_package_too_large":"Le paquet d’assistance anonymisé dépasse la limite de 8 Mio.","backup.error.support_preview_expired":"L’aperçu de la pièce jointe d’assistance a expiré ou n’est plus disponible.","backup.error.support_rate_limited":"Trop de rapports ou d’aperçus d’assistance ont été demandés. Réessayez plus tard.","backup.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","backup.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.invalid_image":"L’image est endommagée ou ne correspond pas à son format.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.unsupported_image":"Utilisez une image PNG, JPEG, WebP ou SVG prise en charge.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.error.in_use":"L'élément est encore utilisé","backup.error.invalid_data":"La requête contient des données invalides","backup.error.invalid_light_entity":"La lumière principale doit être une entité light.* ou switch.*","backup.error.invalid_marker_control":"Référence de contrôle de marqueur invalide","backup.error.invalid_name":"Le nom est invalide","backup.error.invalid_partition_opening_host":"L'ouverture doit conserver sa cloison porteuse","backup.error.invalid_partition_opening_jamb_margin":"L'ouverture ne laisse aucun jambage physique au bout du mur","backup.error.invalid_passage_fields":"Un passage ouvert ne peut pas porter de réglages de porte","backup.error.invalid_space_id":"Espace inconnu","backup.error.invalid_toggle_entity":"L'entité de bascule doit être light.* ou switch.*","backup.error.invalid_value_badge":"Réglages de badge de valeur invalides","backup.error.invalid_value_badge_attribute":"Attribut de badge de valeur invalide","backup.error.invalid_value_badge_position":"Position de badge de valeur invalide","backup.error.invalid_value_badge_source":"Source de badge de valeur invalide","backup.error.invalid_vacuum_map_route":"Vérifiez l'affectation des cartes du robot aux espaces.","backup.error.invalid_value_source":"Source de valeur invalide","backup.error.invalid_value_source_attribute":"Attribut de source de valeur invalide","backup.error.io_error":"Une opération de fichier a échoué sur le serveur","backup.error.not_toggleable":"Cet appareil ne peut pas être basculé","backup.error.nothing_to_repair":"Rien à réparer","backup.error.space_in_use":"L'espace est encore référencé","backup.error.value_badge_source_required":"Le badge de valeur nécessite une source","backup.error.wall_model_client_outdated":"Le plan a été mis à jour ailleurs — rechargez la page","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.decor_images_summary":"Images personnalisées : {objects} objets, {assets} fichiers ; fichiers absents : {missing}.","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.confirm_missing_images":"Je comprends que les images personnalisées absentes resteront des espaces réservés réparables et que les autres fichiers locaux indisponibles seront dissociés.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.reduced_dimensions":"Copie réduite : {w}×{h} px.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const r="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";export{e as dictionary,r as fingerprint}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";var e={"editor.context_actions":"Actions : {object}","editor.tool_options":"Options de l’outil : {tool}","editor.palette":"Palette : {tool}","editor.open_group":"Groupe d’outils : {group}","editor.group_active":"{group} — actif : {item}","editor.disabled_action":"{action} est indisponible : {reason}","editor.loading":"Chargement de l'éditeur…","editor.loading_aria":"L'éditeur de plan est en cours de chargement","editor.load_failed":"Impossible de charger l'éditeur.","editor.refresh_advice":"Actualisez la page et réessayez.","editor.retry_advice":"Vérifiez votre connexion et appuyez à nouveau.","color_picker.title":"Sélecteur de couleur","color_picker.hue":"Teinte","color_picker.saturation":"Saturation","color_picker.value":"Luminosité","color_picker.hex":"Couleur hexadécimale","color_picker.invalid_hex":"Saisissez une couleur hexadécimale à 3 ou 6 chiffres","btn.properties":"Propriétés","btn.keep_as_walls":"Conserver comme murs","btn.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","btn.delete_room_with_walls":"Supprimer la pièce et les murs","title.markup_select":"Sélectionner et modifier les murs, colonnes et contours enregistrés","title.markup_column":"Colonne : cliquez sur un point de la grille pour placer une colonne carrée","markup.partition":"Cloison","markup.column":"Colonne","markup.hint_column":"cliquez sur un point de la grille pour placer une colonne carrée","history.draft_segment":"Ajouter un segment de brouillon de pièce","history.draft_merge":"Relier des contours de pièce inachevés","history.draft_segment_delete":"Supprimer un segment de brouillon de pièce","history.column_add":"Ajouter une colonne","history.physical_edit":"Modifier l’objet physique","history.physical_delete":"Supprimer l’objet physique","history.physical_move":"Déplacer l’objet physique","history.contour_to_partitions":"Convertir le contour en murs fermés","history.wall_chain_finish":"Terminer la chaîne de murs","history.wall_face_batch":"Créer des pièces à partir des murs","toast.column_duplicate":"Une colonne avec le même centre et la même dimension extérieure existe déjà","confirm.delete_draft_title":"Supprimer le contour inachevé ?","confirm.delete_draft_body":"Tout le contour de pièce inachevé sera supprimé. L’annulation ne modifie rien.","confirm.delete_draft_segment_title":"Supprimer le segment de contour ?","confirm.delete_draft_segment_body":"Ce segment de brouillon sera supprimé. Le contour restant peut se scinder en deux.","physical.partition_properties":"Propriétés de la cloison","physical.column_properties":"Propriétés de la colonne","physical.draft_properties":"Propriétés du segment de brouillon","physical.shape":"Forme","physical.square":"Carré","physical.circle":"Cercle","physical.diameter":"Diamètre","physical.side":"Côté","physical.rotation":"Angle de rotation","physical.length":"Longueur","physical.allowed_range":"Autorisé : {min}–{max} {unit}","physical.delete_segment":"Supprimer le segment","physical.delete_draft":"Supprimer tout le contour","physical.column_size_title":"Côté extérieur de la colonne carrée placée par le clic (1–150 cm).","card.title":"Plan de la maison","count.devices":"{n} app.","empty.no_spaces":"Aucun espace pour le moment.","empty.add_first":"Ajoutez le premier espace et importez un plan.","empty.install":"Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».","btn.add_space":"Ajouter un espace","btn.cancel":"Annuler","btn.save":"Enregistrer","btn.close":"Fermer","btn.delete":"Supprimer","btn.edit":"Modifier","btn.open_in_ha":"Ouvrir dans HA","btn.reset":"Réinitialiser","btn.attach":"Joindre…","btn.upload":"Importer…","btn.replace":"Remplacer…","title.zoom_in":"Zoomer","title.zoom_out":"Dézoomer","title.zoom_fit":"Tout ajuster","title.configure_space":"Configurer l’espace","title.add_space":"Ajouter un espace","title.markup_add":"Murs : tracez une chaîne continue ; Maj verrouille à 45°. Changer d’outil ou quitter l’éditeur termine la chaîne en murs indépendants","title.markup_merge":"Fusionner : cliquez sur une pièce, puis sur la pièce voisine avec laquelle elle partage un mur","title.markup_split":"Diviser une pièce : cliquez sur la pièce, puis sur deux points de ses murs","title.markup_delroom":"Supprimer une pièce : cliquez à l’intérieur de la pièce puis confirmez","title.need_plan":"Importer une image de plan","markup.add":"Murs","markup.merge":"Fusionner","markup.split":"Diviser","markup.resize":"Redimensionner","title.markup_resize":"Redimensionner les pièces : faites glisser une poignée disponible sur un mur horizontal ou vertical","markup.hint_resize":"faites glisser une poignée de mur disponible · le mur s’arrête à la première position non sûre · Échap annule · Ctrl+Z — annuler","resize.disabled.diagonal":"Ce mur est incliné ; le redimensionnement ne prend en charge que les murs horizontaux et verticaux","resize.disabled.side-angle":"Un mur adjacent est incliné ; le redimensionnement exige que les deux murs adjacents rencontrent ce mur à angle droit","resize.disabled.duplicate-physical-wall":"Une cloison distincte, un contour inachevé ou une colonne chevauche ce mur ; supprimez-la ou déplacez-la avant de redimensionner","resize.disabled.partial-shared":"Une pièce voisine n’utilise qu’une partie de ce mur ; il ne peut donc pas être déplacé en toute sécurité comme un mur partagé unique","resize.disabled.unequal-shared":"La pièce voisine utilise des extrémités ou une longueur de mur différentes ; les murs ne peuvent donc pas être déplacés ensemble en toute sécurité","resize.disabled.multiple-rooms":"Déplacer ce mur affecterait plus de deux pièces","resize.disabled.thickness-conflict":"L’épaisseur du mur ne peut pas être conservée en toute sécurité","resize.disabled.opening-conflict":"Une ouverture sur ce mur ne peut pas être déplacée en toute sécurité","resize.disabled.invalid-geometry":"Ce mur ne peut pas être redimensionné sans modifier la structure du plan","resize.limit_stopped":"Mur arrêté : l'étape suivante enfreindrait une limite de jonction","resize.commit_failed":"Le redimensionnement a été annulé car le plan final n’a pas passé le contrôle de géométrie","resize.preview_failed":"Le redimensionnement s’est arrêté à la dernière position sûre car la géométrie du plan ne pouvait pas être préservée","markup.opening":"Ouverture","markup.delete_room":"Supprimer la pièce","history.undo":"Annuler","history.redo":"Rétablir","history.undo_named":"Annuler : {name} (Ctrl+Z)","history.redo_named":"Rétablir : {name} (Ctrl+Maj+Z / Ctrl+Y)","history.undo_empty":"Rien à annuler","history.redo_empty":"Rien à rétablir","history.undone":"Annulé : {name}","history.redone":"Rétabli : {name}","history.add_room":"Créer une pièce","history.split_room":"Diviser la pièce","history.merge_rooms":"Fusionner les pièces","history.resize_room":"Redimensionner la pièce","history.wall_thickness":"Modifier l’épaisseur du mur","history.add_opening":"Ajouter une ouverture de porte ou de fenêtre","history.edit_opening":"Modifier l’ouverture","history.move_opening":"Déplacer l’ouverture","history.delete_opening":"Supprimer l’ouverture","history.delete_room_keep_walls":"Supprimer la pièce, conserver les murs","history.delete_room_with_walls":"Supprimer la pièce et les murs","history.decor_add":"Ajouter un objet décoratif","history.decor_edit":"Modifier l’objet décoratif","history.decor_move":"Déplacer l’objet décoratif","history.decor_transform":"Transformer l’objet décoratif","history.decor_delete":"Supprimer l’objet décoratif","history.backdrop_transform":"Transformer l’arrière-plan du plan","history.device_move":"Déplacer {name}","history.device_stale":"L’historique des positions n’est plus applicable et a été effacé","opening.new":"Nouvelle ouverture","opening.edit":"Ouverture","opening.door":"Porte","opening.window":"Fenêtre","opening.gate":"Portail","opening.passage":"Passage ouvert","opening.passage_binding_warning":"L’enregistrement supprimera le capteur d’ouverture/fermeture et la serrure.","opening.invalid_passage_fields":"Le passage ouvert du plan « {room} » possède des paramètres non pris en charge : {fields}.","opening.type_label":"Type","opening.length_label":"Longueur, cm","opening.contact_label":"Capteur d’ouverture/fermeture","opening.lock_label":"Serrure","opening.none":"— aucun —","opening.search_ph":"Rechercher : nom ou ID d’entité…","opening.invert":"Inverser ouvert/fermé","opening.flip_h":"Charnière sur l’autre montant","opening.flip_v":"Ouverture de l’autre côté","opening.open":"Ouvert","opening.closed":"Fermé","opening.locked":"Verrouillé","opening.unlocked":"Déverrouillé","opening.state_unknown":"indisponible","opening.no_entities":"Aucun capteur associé — symbole statique sur le plan.","toast.opening_no_wall":"Cliquez près d’un mur de pièce ou d’un mur indépendant","opening.host_partition":"Mur indépendant","opening.partition_orphan":"Le mur indépendant de cette ouverture n’existe plus","opening.partition_jamb_margin":"Laissez au moins {distance} entre l’ouverture et l’extrémité du mur","opening.rebind_partition":"Associer à un autre mur indépendant","confirm.delete_partition_openings_title":"Supprimer le mur et les ouvertures ?","confirm.delete_partition_openings_body":"Ce mur contient {count} ouverture(s). Elles seront supprimées avec lui.","confirm.delete_partition_openings_item":"• {type}, {length}","markup.hint_points":"points : {n} · Maj — pas de 45° · Échap — terminer la chaîne · Ctrl+Z — annuler un point · fermer une zone propose de créer une pièce","markup.hint_start":"cliquez sur un point de la grille pour commencer une chaîne de murs","tip.lqi":"signal Zigbee moyen :","tip.area":"surface : {value}","info.device_header":"Appareil sur le plan","info.model":"Modèle","info.state":"État","info.link":"Lien","info.manuals":"Manuels","info.none":"Aucune information supplémentaire","marker.new_device":"Nouvel appareil","marker.name_label":"Nom (affiché sur le plan)","marker.name_ph":"Nom","marker.binding_label":"Associer à un appareil HA","marker.binding_disabled":"désactivé dans Home Assistant","marker.virtual_option":"Appareil virtuel (sans association)","marker.search_ph":"Rechercher un appareil / groupe…","marker.nothing_found":"aucun résultat","marker.room_label":"Pièce","marker.room_override":" (placement forcé)","marker.room_choose":"— sélectionner une pièce —","marker.room_auto":"— selon la zone de l’appareil (auto) —","marker.icon_label":"Icône","marker.icon_ph":"mdi:… (vide = auto)","marker.display_label":"Affichage","display.badge":"Icône + état","display.icon_ripple":"Icône + état et activité","display.static_icon":"Icône toujours statique","marker.display_hint_badge":"L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.","marker.display_hint_icon_ripple":"L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.","marker.display_hint_value":"Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.","marker.display_hint_static_icon":"Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.","marker.static_alarm_warning":"Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.","marker.preview.title":"Aperçu de l’affichage","marker.preview.actual":"Actuel","marker.preview.example":"Exemple","marker.preview.integration":"Fourni par","marker.preview.source":"Source d’affichage","marker.preview.current_state":"État actuel","marker.preview.result":"Sur le plan","marker.preview.details":"Détails de la source","marker.preview.select_source":"Choisissez un appareil ou une entité Home Assistant pour voir son affichage réel.","marker.preview.unknown_provider":"Intégration inconnue","marker.preview.virtual_provider":"House Plan · appareil virtuel","marker.preview.no_source":"Aucune source active","marker.preview.no_state":"Aucun état actuel","marker.preview.mixed_states":"Plusieurs états différents","marker.preview.multiple_sources":"{n} sources","marker.preview.more_sources":"+{n} de plus","marker.preview.scaled":"L’aperçu est redimensionné à {n}% pour tenir dans la zone. La taille enregistrée reste inchangée.","marker.preview.demo_short":"Afficher une activité brève","marker.preview.demo_continuous":"Afficher une activité continue","marker.preview.stop_continuous":"Arrêter l’activité continue","marker.preview.demo_short_notice":"Exemple d’activité brève ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_continuous_notice":"Exemple d’activité continue ; l’état réel de l’appareil reste inchangé.","marker.preview.demo_already_visible":"L’état actuel affiche déjà un véritable effet d’activité ou d’alarme.","marker.preview.reduced_motion":"La réduction des animations est activée sur le système ; l’activité normale est donc affichée sous forme de point.","marker.preview.reason.neutral":"Fond sombre neutre","marker.preview.reason.working":"Fond jaune : l’appareil fonctionne actuellement","marker.preview.reason.working_activity":"Fond jaune et effet d’activité : l’appareil fonctionne actuellement","marker.preview.reason.open":"Fond orange : physiquement ouvert ou déverrouillé","marker.preview.reason.cover_icon_state":"Fond neutre ; l’entité de volet contrôle l’état et l’icône affichés","marker.preview.reason.presence":"Effet d’activité tant qu’une présence est détectée","marker.preview.reason.event":"Effet d’activité bref après un événement détecté","marker.preview.reason.transition":"Effet d’activité pendant que l’appareil se déplace ou change d’état","marker.preview.reason.media_neutral":"Les appareils multimédias restent sur un fond sombre neutre tant qu’ils sont disponibles","marker.preview.reason.unavailable":"Fond neutre atténué : indisponible ou éteint","marker.preview.reason.alarm":"Fond d’alarme rouge ; l’indication d’alarme est toujours affichée","marker.preview.reason.live_states_disabled":"Le style selon les états en direct est désactivé pour cette carte","marker.preview.reason.value_no_state":"Aucune valeur d’état exploitable ; l’icône est affichée à la place","marker.preview.reason.value_ambiguous_sources":"Plusieurs sources de valeur possibles ; l’icône est affichée à la place","marker.preview.reason.value_non_scalar":"La source n’a pas renvoyé de valeur simple ; l’icône est affichée à la place","marker.preview.reason.value_virtual":"Un appareil virtuel n’a aucune valeur Home Assistant ; l’icône est affichée à la place","marker.preview.reason.vacuum_live_plan_only":"La position et la trajectoire en direct de l’aspirateur ne sont disponibles que sur le plan complet","marker.preview.reason.hidden_design_preview":"L’appareil est masqué sur le plan ; cet aperçu de conception reste visible","marker.preview.reason.composite_power_source":"L’état provient de l’entité d’alimentation de l’appareil ; les interrupteurs auxiliaires sont ignorés","marker.preview.reason.activity_display_disabled":"Le mode d’affichage sélectionné n’affiche pas les effets d’activité habituels","marker.preview.reason.ha_disabled":"L’association Home Assistant est désactivée et sera masquée sur le plan","marker.preview.reason.orphaned":"L’association Home Assistant enregistrée est introuvable","marker.preview.reason.static_icon":"Mode statique : l’état de l’appareil ne modifie pas l’icône","marker.activity_color":"Couleur de la pulsation d’activité","marker.ripple_size":"Taille de la pulsation d’activité","marker.activity_alarm_note":"La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.","marker.pulse_a11y_alarm":"Alarme","marker.pulse_a11y_event":"Événement récent","marker.pulse_a11y_presence":"Présence détectée","marker.pulse_a11y_transition":"Changement d’état","marker.pulse_a11y_running":"En fonctionnement","marker.state_a11y_neutral":"État par défaut","marker.state_a11y_working":"En fonctionnement","marker.state_a11y_open":"Ouvert","marker.state_a11y_locked":"Verrouillé","marker.state_a11y_unlocked":"Déverrouillé","marker.state_a11y_alarm":"Alarme","marker.state_a11y_unavailable":"Indisponible","marker.lqi_a11y_low":"LQI {value}, signal faible","marker.lqi_a11y_mid":"LQI {value}, signal moyen","marker.lqi_a11y_high":"LQI {value}, signal fort","marker.size_label":"Taille / rotation de l’icône","marker.angle_label":"Rotation","marker.model_label":"Modèle","marker.model_ph":"ex. Aqara T&H","marker.link_label":"Lien","marker.desc_label":"Description","marker.desc_ph":"Notes, caractéristiques…","marker.manuals_label":"Manuels (PDF, etc.)","marker.sub_device":"appareil","marker.sub_z2m_group":" · groupe Z2M","marker.sub_group":"groupe","marker.sub_helper":"assistant","space.new":"Nouvel espace","space.header":"Espace","space.title_label":"Titre","space.title_ph":"ex. Garage","space.plan_label":"Plan (arrière-plan)","space.no_plan":"aucune image de plan","space.plan_alt":"plan","room.new":"Nouvelle pièce","room.name_label":"Nom affiché","room.name_ph":"ex. Terrasse","room.area_label":"Zone Home Assistant (non attribuée)","room.no_area_option":"— aucune zone —","room.default_name":"Pièce","device.unnamed":"sans nom","device.light_group":"groupe de lumières","device.fallback":"appareil","device.virtual":"appareil virtuel","confirm.delete_room_title":"Supprimer la pièce « {name} » ?","confirm.delete_room_body":"Choisissez si les murs physiques propres à la pièce doivent être conservés. Les murs partagés avec une autre pièce sont toujours conservés.","confirm.remove_marker_title":"Supprimer l’appareil du plan ?","confirm.remove_marker_body":"L’appareil disparaîtra complètement et ne contribuera plus aux données du plan. Vous pourrez l’ajouter de nouveau plus tard.","confirm.erase_decor":"Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.","confirm.delete_space_title":"Supprimer l’espace ?","confirm.delete_space_body":"L’espace, toutes ses pièces et tout le tracé du plan seront supprimés.","confirm.delete_space_vac_routes":"Les affectations de cartes de robots seront aussi supprimées : {count}.","space.delete_blocked":"Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.","toast.pos_save_failed":"Échec de l’enregistrement de la position : {err}","toast.no_entity":"L’appareil n’a aucune entité adaptée","toast.ha_disabled_action":"Un objet Home Assistant désactivé ne peut pas être utilisé sur le plan.","toast.ha_disabled_show_device":"Un appareil désactivé dans Home Assistant ne peut pas être affiché sur le plan. Activez-le d’abord dans Home Assistant.","toast.ha_disabled_show_entity":"Une entité désactivée dans Home Assistant ne peut pas être affichée sur le plan. Activez-la d’abord dans Home Assistant.","toast.ha_disabled_add":"Un objet Home Assistant désactivé ne peut pas être ajouté au plan. Activez-le d’abord dans Home Assistant.","toast.ha_binding_unverified":"L’état de l’objet n’a pas pu être vérifié via le registre Home Assistant. L’affichage et les actions sont temporairement indisponibles.","toast.markup_needs_server":"Le tracé est disponible une fois la configuration déplacée sur le serveur","toast.space_order_changed":"L’ordre a changé. Si une carte fixe son étage par numéro, vérifiez ces tableaux de bord.","toast.conflict":"La configuration a été modifiée dans une autre fenêtre — données actualisées, répétez votre dernière action","toast.cfg_save_failed":"Échec de l’enregistrement de la configuration : {err}","toast.room_overlap":"Le contour chevauche la pièce « {name} » — les pièces ne doivent pas se chevaucher","toast.contour_cannot_close":"Le contour ne peut pas être fermé car il est dégénéré ou s’auto-intersecte","toast.merge_not_adjacent":"Seules les pièces partageant un mur peuvent être fusionnées","toast.rooms_merged":"Pièces fusionnées dans « {name} »","toast.split_pick_wall":"Commencez la découpe sur le mur de la pièce","toast.split_bad_cut":"La découpe doit aller d’un mur à l’autre à l’intérieur de la pièce, sans croiser de mur ni se croiser elle-même","merge.header":"Fusionner les pièces","merge.hint":"La pièce fusionnée conserve un nom et une zone. L’autre zone est libérée — ses appareils quittent le plan jusqu’à ce qu’une autre pièce la récupère.","merge.keep":"Conserver","merge.no_area":"aucune zone","toast.room_saved":"Pièce enregistrée ({n}). Appareils ajoutés : {added}. Tracez la suivante ou quittez le mode tracé.","toast.room_saved_no_area":"Pièce enregistrée ({n}, sans zone). Tracez la suivante ou quittez le mode tracé.","toast.marker_needs_server":"La modification des appareils est disponible une fois la configuration déplacée sur le serveur","toast.virtual_name_required":"Saisissez un nom pour l’appareil virtuel","toast.marker_saved":"Appareil enregistré","toast.marker_removed":"Appareil supprimé du plan","toast.integration_missing":"L’intégration House Plan n’est pas installée — gestion indisponible","toast.plan_formats":"Formats pris en charge : SVG, PNG, JPG, WebP","toast.plan_required":"Importez un plan — il est obligatoire","toast.space_added_onboard":"Espace ajouté. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","toast.space_added":"Espace ajouté","toast.space_saved":"Espace enregistré","toast.space_deleted":"Espace supprimé","toast.delete_failed":"Échec de la suppression : {err}","toast.error":"Erreur : {err}","toast.toggle_target_unavailable":"La cible « {name} » est indisponible — aucune action n’a été effectuée","toast.toggle_targets_unavailable":"Cibles indisponibles : {names}. Aucune action n’a été effectuée","toast.file_failed":"Le fichier « {name} » n’a pas été importé : {err}","toast.files_attached":"Fichiers joints : {n}","err.unknown":"erreur inconnue","err.code":"code {code}","err.too_large":"fichier supérieur à {mb} Mo","err.bad_ext":"type non pris en charge (PDF/image attendu)","err.unauthorized":"droits administrateur requis","editor.title":"Titre","editor.default_floor":"Espace initial","editor.default_floor_missing":"L’espace initial « {id} » n’existe plus. Choisissez un autre espace.","editor.floor":"Espace fixe","editor.floor_none":"— non fixé —","editor.floor_index":"Index YAML {index} (conservé)","fixed_floor.loading":"Chargement de l’espace fixe…","fixed_floor.invalid_title":"L’espace fixe est indisponible","fixed_floor.invalid_body":"Vérifiez floor dans la configuration de la carte. Valeur configurée : {value}","editor.icon_size":"Taille des icônes, % de la largeur du plan","editor.show_temperature":"Afficher la température","editor.live_states":"États en direct (on/off, ouvert…)","editor.light_pools":"Halos lumineux et ombres des murs","editor.show_signal":"Afficher le signal Zigbee (LQI)","editor.language":"Langue de l’interface","editor.lang_auto":"Auto (profil HA)","title.icon_rules":"Règles d’icônes : quelle icône MDI attribuer aux appareils selon leur nom","rules.title":"Règles d’icônes","rules.hint":"Les règles sont testées de haut en bas sur « nom de l’appareil + modèle » (expression régulière insensible à la casse) ; la première correspondance gagne. Si rien ne correspond, la classe d’appareil de l’entité est utilisée, puis l’icône générique.","rules.pattern_ph":"regex, ex. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Ajouter une règle","rules.reset":"Réinitialiser par défaut","rules.test_ph":"Tester un nom d’appareil…","rules.invalid":"expression régulière invalide","rules.saved":"Règles d’icônes enregistrées","btn.up":"Monter","btn.down":"Descendre","tap.info":"Fiche de l’appareil","tap.more_info":"Boîte de dialogue Plus d’infos de HA","tap.toggle":"Basculer l’état","marker.tap_label":"Action au toucher pour cet appareil","tap.toggle_note":"L’état déterminé est affiché ci-dessous. Les appareils sécurisés ne sont jamais basculés depuis le plan.","import.title":"Créer des espaces à partir des étages HA","import.hint":"Votre Home Assistant connaît déjà ces étages. Sélectionnez ceux à transformer en espaces du plan — vous importerez ensuite une image de plan pour chacun. Les pièces sont ensuite tracées manuellement sur le plan.","import.start":"Créer {n} espace(s)","import.manual":"Commencer de zéro","import.progress":"Étage {i} sur {n}","import.done":"Espaces créés. Tracez les pièces : cliquez sur les points de la grille et fermez le contour.","btn.skip":"Passer","space.scale_label":"Échelle (taille d’une cellule de grille)","space.cell_cm.help":"Relie la grille aux dimensions réelles : les longueurs, les surfaces, l’épaisseur des murs, la taille des ouvertures et le rayon du halo en dépendent. Une modification ultérieure change les dimensions calculées de tout l’espace sans déplacer ses points sur le plan.","space.cell_cm.help.aria":"Aide : échelle de l’espace","space.scale_unit":"cm par cellule","space.scale_unit_imperial":"po par cellule","space.display_section":"Affichage","space.show_borders":"Toujours afficher les contours des pièces","space.zero_wall_style":"Murs d’épaisseur nulle","space.zero_wall_style.help":"Les murs d’épaisseur nulle en pointillés laissent passer le halo et les rayons du soleil, tandis que les murs pleins les bloquent comme une barrière sans surface. Ce choix s’applique à tous ces murs de l’espace, même si leur ligne est masquée en mode Vue.","space.zero_wall_style.help.aria":"Aide : style des murs d’épaisseur nulle","space.zero_wall_dashed":"Pointillés","space.zero_wall_solid":"Plein","space.show_names":"Afficher les noms des pièces (faire glisser pour déplacer)","space.room_color":"Couleur du contour et du nom","space.opacity":"Opacité","space.fill_label":"Remplissage de la pièce","space.fill_mode.help":"« Couleur personnalisée » définit le style, tandis que « Lumière », « Température » et « LQI » utilisent les données actuelles de Home Assistant. Le halo s’active séparément.","space.fill_mode.help.aria":"Aide : mode de remplissage des pièces","fill.none":"Aucun","fill.lqi":"Signal Zigbee","fill.light":"Lumières","fill.custom":"Couleur personnalisée","space.custom_fill":"Couleur de remplissage","space.source_file":"J’ai une image de plan","space.source_draw":"Aucune image — je tracerai les pièces à la main","fill.temp":"Température","tip.temp_avg":"température moyenne :","tip.hum_avg":"humidité moyenne :","space_card.button":"Ouvrir le plan de l’espace","space_card.not_found":"Espace « {id} » introuvable","space_card.loading":"Chargement…","continuity.restore_plan":"Restauration du plan…","continuity.restore_connection":"Restauration de la connexion aux appareils…","continuity.retry":"Réessayer","editor.space":"Espace","editor.framing":"Cadrage","editor.fit_content":"Tout le contenu visible","editor.fit_house":"Ajusté à la géométrie de la maison","editor.show_button":"Afficher le bouton","editor.button_label":"Libellé du bouton","editor.button_target":"Chemin du tableau de bord cible","marker.sub_entity":"entité","title.general_settings":"Paramètres généraux","gs.title":"Paramètres généraux","gs.light_group":"Remplissage : lumières","gs.light_on":"Lumières allumées","gs.light_off":"Toutes les lumières éteintes","gs.temp_group":"Remplissage : température","gs.temp_cold":"Froid","gs.temp_ok":"Confortable","gs.temp_hot":"Chaud","gs.lqi_group":"Remplissage : signal Zigbee","gs.lqi_low":"Signal faible","gs.lqi_high":"Signal fort","gs.reset":"Réinitialiser par défaut","gs.saved":"Paramètres généraux enregistrés","space.show_lqi":"Afficher le signal Zigbee (LQI) à côté des appareils","space.hide_decor":"Masquer la couche décorative","space.hide_decor_tip":"Les lignes, formes, étiquettes et meubles restent à leur place — visibles dans l’éditeur d’arrière-plan, mais pas sur le plan.","space.hide_openings":"Masquer les ouvertures","space.hide_openings_tip":"Les symboles de portes, fenêtres et portails ne sont pas dessinés, mais les ouvertures continuent de fonctionner : la lumière les traverse, le soleil entre par les fenêtres et les capteurs de contact continuent de les ouvrir. L’éditeur de plan les affiche toujours.","gs.light_none":"Aucune source lumineuse","mode.plan":"Éditeur de plan","mode.devices":"Éditeur d’appareils","display.value":"Valeur + état","marker.subarea":"aucune zone, manuel","device.new":"Nouvel appareil — ouvrez son éditeur pour fermer","opening.unlock_action":"Déverrouiller","opening.lock_action":"Verrouiller","opening.lock_pending":"En cours…","title.close_editor":"Fermer l’éditeur (retour à la vue)","title.add_device":"Ajouter un appareil au plan","devbar.add":"Ajouter","devbar.rules":"Règles d’icônes","device_inbox.button":"Appareils","device_inbox.title":"Appareils sur le plan","device_inbox.search":"Rechercher appareils, entités et intégrations…","device_inbox.add_virtual":"Ajouter un appareil virtuel","device_inbox.tab_on_plan":"Sur le plan","device_inbox.tab_available":"Disponibles","device_inbox.tab_hidden":"Masqués","device_inbox.tab_readd":"De nouveau disponibles","device_inbox.only_new":"Nouveaux uniquement","device_inbox.show_entities":"Afficher les entités","device_inbox.show_hidden":"Afficher les masqués sur le plan","device_inbox.show_hidden.help":"Affiche temporairement les marqueurs masqués et désactivés sous forme de fantômes réservés à l’éditeur. La visibilité enregistrée des appareils ne change pas.","device_inbox.show_hidden.help.aria":"Aide : afficher les appareils masqués sur le plan","device_inbox.new":"Nouveau","device_inbox.find":"Trouver sur le plan","device_inbox.edit":"Modifier","device_inbox.hide":"Masquer","device_inbox.show":"Afficher","device_inbox.add":"Ajouter","device_inbox.readd":"Ajouter à nouveau","device_inbox.hide_available":"Masquer de la liste","device_inbox.show_more":"Afficher plus","device_inbox.more_actions":"Plus d'actions","device_inbox.find_hidden_hint":"Activez d'abord « Afficher les masqués sur le plan »","device_inbox.show_disabled":"Activez cette liaison dans Home Assistant avant de l'afficher","device_inbox.empty_on_plan":"Aucun appareil correspondant n'est encore sur le plan.","device_inbox.empty_available":"Aucun appareil disponible à ajouter.","device_inbox.empty_hidden":"Il n'y a aucun appareil masqué.","device_inbox.empty_readd":"Aucun appareil supprimé n'est disponible pour un nouvel ajout.","device_inbox.reason_visible_auto":"Découvert automatiquement","device_inbox.reason_visible_explicit":"Ajouté explicitement","device_inbox.reason_manual_hidden":"Masqué par l'utilisateur","device_inbox.reason_automatic_hidden":"Masqué automatiquement","device_inbox.reason_service_entry":"Entrée de registre de service uniquement","device_inbox.reason_excluded_integration":"Intégration « {integration} » exclue par les filtres de découverte","device_inbox.reason_excluded_domain":"Type d'entité non spatial","device_inbox.reason_grouped_light":"Représenté par un groupe de lumières de la pièce","device_inbox.reason_represented_by_parent":"Représenté par son appareil parent","device_inbox.reason_removed":"Précédemment retiré du plan","device_inbox.reason_available":"Disponible pour ajout au plan","device_inbox.reason_no_bound_room":"La zone HA n'est liée à aucune pièce du plan","device_inbox.status_ha_disabled":"Désactivé dans Home Assistant","device_inbox.status_orphaned":"La liaison n'existe plus dans Home Assistant","device_inbox.status_unverified":"Le registre Home Assistant est temporairement indisponible","device_inbox.saved":"Liste des appareils mise à jour","device_inbox.filters_excluded":"Intégrations exclues","device_inbox.filters_group_lights":"Regrouper les lumières de la pièce en un marqueur","device_inbox.filters_preview_appear":"apparaîtra : {count}","device_inbox.filters_preview_hide":"disparaîtra : {count}","device_inbox.filters_preview_lights":"lumières concernées : {count}","device_inbox.filters_reset":"Rétablir les recommandées","device_inbox.filters_save":"Enregistrer les filtres","device_inbox.filters_search_ph":"Ajouter une intégration…","device_inbox.filters_title":"Filtres de découverte","space.roomcard_section":"La carte de pièce affiche :","space.label_temp":"Température","space.label_hum":"Humidité","space.label_lqi":"Signal Zigbee moyen","space.label_light":"Lumières allumées/éteintes","roomcard.light_on":"Allumé","roomcard.light_off":"Éteint","roomcard.light_partial":"{on} sur {total}","toast.split_pick_inside":"Les points intermédiaires de découpe doivent se trouver à l’intérieur de la pièce","mode.decor":"Éditeur d’arrière-plan","decor.select":"Sélectionner","decor.line":"Ligne","decor.rect":"Rectangle","decor.ellipse":"Ovale","decor.text":"Texte","decor.erase":"Effacer","decor.erase_confirm_title":"Effacer l’objet ?","decor.color":"Couleur","decor.width":"Épaisseur de ligne","decor.line_style":"Style de ligne","decor.line_style_solid":"Plein","decor.line_style_dashed":"Pointillés","decor.fill":"Remplissage","decor.fill_color":"Couleur de remplissage","decor.length":"Longueur","decor.size":"Taille","decor.flip_h":"Retourner horizontalement","decor.flip_v":"Retourner verticalement","decor.angle":"Angle de rotation, °","decor.text_size":"Taille du texte","decor.backdrop_properties":"Propriétés de l’arrière-plan du plan","decor.text_title":"Étiquette de texte","decor.object_title":"Modifier {kind}","decor.text_label":"Texte","decor.live_group":"Insérer une variable HA","decor.live_entity":"Entité","decor.live_entity_ph":"choisir une entité","decor.live_attr":"Valeur","decor.live_attr_ph":"choisir l’état ou un attribut","decor.live_state":"État","decor.backdrop":"Image d’arrière-plan","decor.backdrop_hint":"Faites glisser pour déplacer ; tirez un coin pour redimensionner ; utilisez la poignée supérieure pour faire pivoter. Maj modifie les proportions ou libère l’angle.","decor.backdrop_reset":"Réinitialiser l’image","decor.backdrop_reset_done":"L’image a retrouvé sa position et sa taille d’origine","marker.icon_auto":"Auto : {icon} (selon les règles d’icônes ; choisissez-en une pour remplacer)","marker.icon_pin_auto":"Épingle","mode.plan_tip":"Éditeur de plan — la géométrie du logement : dessiner et diviser/fusionner les pièces, les associer aux zones HA, placer portes, fenêtres et portails, déplacer les cartes de pièces, régler l’échelle","mode.devices_tip":"Éditeur d’appareils — tout ce qui concerne les icônes : faire glisser pour positionner, cliquer pour modifier l’association/l’icône/l’affichage, ajouter des appareils virtuels, règles d’icônes","mode.decor_tip":"Éditeur d’arrière-plan — décoration purement visuelle sous le plan : lignes, rectangles, ovales et étiquettes de texte qui ne réagissent jamais aux clics","space.glow_enabled":"Halo des sources lumineuses","gs.glow_group":"Halo des sources lumineuses","gs.glow_base":"Obscurité de la maison","gs.glow_light":"Couleur / intensité de lumière par défaut","gs.wall_group":"Murs","gs.wall_fill":"Remplissage des murs","gs.glow_radius":"Rayon du halo","gs.glow_radius.help":"Définit le rayon général du halo en mètres ou en pieds ; le rayon propre à un appareil remplace cette valeur.","gs.glow_radius.help.aria":"Aide : rayon général du halo","gs.unit_m":"m","gs.unit_ft":"pi","marker.controls_label":"Contrôle d’autres sources lumineuses","marker.controls.help":"Avec l’action « Basculer l’état », les sources répertoriées basculent avec ce marqueur. Leur halo reste sur leurs propres marqueurs et ce lien ne transforme pas à lui seul le marqueur de contrôle en source lumineuse.","marker.controls.help.aria":"Aide : contrôler d’autres sources lumineuses","marker.controls_filter":"Rechercher des lumières et interrupteurs…","info.controls":"Contrôle","marker.glow_radius_label":"Rayon du halo","marker.glow_radius.help":"Définit le rayon du halo en mètres ou en pieds ; une valeur vide utilise le rayon des paramètres généraux.","marker.glow_radius.help.aria":"Aide : rayon du halo","markup.wallthick":"Épaisseur","markup.select":"Sélectionner","title.markup_wallthick":"Épaisseur — cliquez sur un mur pour définir son épaisseur de 0 à 100 cm.","markup.hint_wallthick":"cliquez sur un mur · Échap ferme sans appliquer","wallthick.field":"Épaisseur","wallthick.unit_cm":"cm","wallthick.unit_in":"po","wallthick.apply_room":"Appliquer à tous les murs de cette pièce","markup.draw_wall_title":"Épaisseur de chaque nouveau segment de mur (0–100 cm). Les murs partagés conservent la valeur existante.","room.queue_progress":"Pièce {current} sur {total}","toast.wall_rooms_saved":"Pièces créées : {n}","toast.wall_chain_saved":"Chaîne de murs enregistrée","toast.wallthick_pick":"Cliquez sur un mur","toast.wallthick_set":"Épaisseur du mur définie","toast.wallthick_cleared":"Épaisseur du mur supprimée","toast.physical_range":"Saisissez une valeur comprise entre {min} et {max} {unit}","toast.zero_wall_opening_conflict":"Supprimez d’abord l’ouverture sur ce segment de mur.","toast.zero_wall_ambiguous":"Le segment de mur est ambigu. Simplifiez ou ajustez la jonction.","toast.zero_wall_migration_blocked":"L’espace n’a pas été converti : {reason}. Aucune donnée n’a été modifiée.","toast.physical_angle":"Saisissez un angle de rotation compris entre 0° inclus et 90° exclu","toast.physical_limit":"L’espace a atteint la limite pour ce type de géométrie","toast.geometry_unsafe":"Modification annulée : la géométrie des murs n’a pas pu être construite en toute sécurité.","junction.limit_angle":"L'angle entre les murs est trop aigu : {actual}°, minimum {limit}°.","junction.limit_valence":"Trop de murs se rejoignent en un nœud : {actual}, maximum {limit}.","junction.limit_length":"Le segment de mur est trop court : {actual} cm, minimum {limit} cm.","junction.limit_distance":"Les murs et les nœuds sont trop proches : {actual} cm, minimum {limit} cm.","junction.limit_clearance":"Il ne reste plus d'intérieur de pièce : {actual} cm², minimum {limit} cm².","junction.limit_check_failed":"La vérification des jonctions n'a pas pu s'exécuter — la modification n'a pas été enregistrée","toast.wall_model_migration_blocked":"Le modèle de murs n’a pas pu être mis à jour : {reason}. Le plan n’a pas été modifié. Lancez « Optimiser les plans » ; si l’erreur se reproduit, corrigez la géométrie de mur conflictuelle.","toast.wall_model_client_outdated":"Mettez à jour la carte et rechargez la page avant de modifier le plan.","wall_model.reason.invalid-room":"contour de pièce invalide","wall_model.reason.zero-length":"segment de mur de longueur nulle","wall_model.reason.third-owner":"mur partagé par plus de deux pièces","wall_model.reason.duplicate-id":"identifiants de mur en conflit","wall_model.reason.thickness-conflict":"valeurs d’épaisseur de mur en conflit","wall_model.reason.opening-host":"l’ouverture ne peut pas être attribuée à un seul mur","toast.delete_room_pick":"Cliquez à l’intérieur de la pièce que vous souhaitez supprimer","toast.plan_snap_ambiguous":"Zoomez pour choisir le nœud du mur","toast.wall_repair_ambiguous":"Le contour de la pièce possède plusieurs connexions possibles. Zoomez et reliez-le explicitement.","toast.wall_repair_too_large":"L’écart dépasse 2 cm. Reliez les murs avant de créer la pièce.","toast.wall_repair_changed":"La géométrie des murs a changé. Essayez de créer de nouveau la pièce.","toast.opening_on_zero_wall":"Les ouvertures ne peuvent pas être placées sur un mur d’épaisseur nulle","marker.from_ha_option":"Choisir dans la liste HA","marker.show_entities":"Afficher les entités","marker.show_entities_tip":"Ajoute à la liste non seulement les appareils, mais aussi toutes leurs entités","marker.pick_ph":"Choisir un appareil…","room.open_area":"Ouvrir la zone HA","room.fit_action":"Ajuster la pièce {name}","view.volumetric":"Vue volumétrique","view.flat":"Vue plane","kiosk.title":"Tailles pour cet écran","kiosk.hint":"Enregistrées uniquement sur cet appareil — chaque tablette murale ou TV peut avoir ses propres tailles confortables.","kiosk.icon_scale":"Taille des icônes d’appareils","kiosk.font_scale":"Taille du texte des cartes de pièces","editor.kiosk":"Mode appareil mural (kiosque)","editor.cycle":"Changer automatiquement d’espace toutes les N secondes (kiosque, 0 = désactivé)","room.settings_title":"Paramètres de la pièce","room.settings_section":"Paramètres de la pièce (remplacent ceux de l’espace)","room.fill_label":"Remplissage de CETTE pièce","fill.inherit":"Comme l’espace","room.custom_fill_space":"Couleur de l’espace","room.custom_fill_own":"Couleur de la pièce","room.temp_src_label":"Source de température","room.hum_src_label":"Source d’humidité","room.src_average":"Moyenne des capteurs de la pièce (par défaut)","room.src_pick":"Un appareil ou une entité HA spécifique","room.src_ph":"Choisir une source…","toast.room_updated":"Pièce mise à jour","space.card_font":"Taille de police des cartes de pièces (tout l’espace)","room.sizes_section":"Tailles de police","room.name_scale":"Taille du nom de la pièce","room.label_scale":"Taille des mesures","preview.room_name":"Salon","toast.cfg_reload_failed":"Impossible de recharger le plan depuis le serveur : {err}","toast.locale_load_failed":"Impossible de charger le pack de langue. L'anglais est utilisé jusqu'au rechargement de la page.","room.settings_short":"Paramètres de la pièce","room.unnamed":"Pièce sans nom","marker.use_climate_temp":"Inclure la température de l’appareil dans la pièce","marker.use_climate_temp_tip":"Ajoute la valeur current_temperature d’un climatiseur ou thermostat à la moyenne de la pièce. Configurez séparément le badge de valeur externe ci-dessous.","marker.value_badge_title":"Badge de valeur","marker.value_badge.help":"Affiche une valeur sélectionnée à côté de l’icône. Cela n’affecte pas les mesures de la pièce, la lumière, le halo ni l’action au toucher.","marker.value_badge.help.aria":"Aide : badge de valeur de l’appareil","marker.value_badge_enabled":"Afficher un badge de valeur","marker.value_badge_source":"Valeur","marker.value_badge_source.help":"Choisissez un état précis, un attribut pris en charge ou une valeur dérivée de cet appareil.","marker.value_badge_source.help.aria":"Aide : source du badge de valeur","marker.value_badge_position":"Position","marker.value_badge_position.help":"Le côté choisi reste fixe et ne bascule pas automatiquement au bord du plan.","marker.value_badge_position.help.aria":"Aide : position du badge de valeur","marker.value_badge_right":"À droite","marker.value_badge_bottom":"En dessous","marker.value_badge_left":"À gauche","marker.value_badge_top":"Au-dessus","marker.value_badge_empty":"Cet appareil n’a aucune valeur disponible","marker.value_badge_static":"Une icône statique n’affiche pas de valeurs en direct. Le paramètre est conservé.","marker.value_badge_missing":"Source indisponible","marker.value_badge_missing_hint":"La source enregistrée est actuellement indisponible. Conservez-la, désactivez le badge ou choisissez une autre valeur.","marker.value_badge_duplicate":"La même valeur est déjà affichée dans l’icône.","marker.value_badge_state":"État · {name}","marker.value_badge_marker_state":"État de la source lumineuse · {name}","marker.value_badge_lqi":"Qualité moyenne du signal Zigbee","marker.value_badge_attr_current_temperature":"Température actuelle · {name}","marker.value_badge_attr_temperature":"Température cible · {name}","marker.value_badge_attr_current_humidity":"Humidité actuelle · {name}","marker.value_badge_attr_humidity":"Humidité · {name}","marker.value_badge_attr_current_position":"Position · {name}","marker.value_badge_attr_percentage":"Vitesse · {name}","marker.value_badge_attr_brightness":"Luminosité · {name}","marker.value_badge_attr_volume_level":"Volume · {name}","marker.value_badge_attr_battery_level":"Batterie · {name}","marker.value_badge_attr_fan_speed":"Vitesse du ventilateur · {name}","marker.value_source":"Source de la valeur","marker.value_source.help":"Choisissez la valeur qui remplace l’icône. L’action au toucher ne change pas.","marker.value_source.help.aria":"Aide : source de la valeur dans le marqueur","marker.value_source_auto":"Automatique (comme avant)","marker.value_source_missing_hint":"La source enregistrée est indisponible. Le marqueur affiche — jusqu’à son retour ou jusqu’au choix d’une autre valeur.","marker.light_role_label":"Cet appareil est-il une source lumineuse ?","marker.light_role.help":"« Auto » utilise le rôle déterminé de l’appareil lié, « Toujours » force sa propre source et « Jamais » l’exclut ; les lumières liées ci-dessus restent indépendantes.","marker.light_role.help.aria":"Aide : cet appareil est-il une source lumineuse","marker.light_role_auto_yes":"Auto (source lumineuse)","marker.light_role_auto_no":"Auto (pas une source lumineuse)","marker.light_role_always":"Toujours une source lumineuse","marker.light_role_never":"Jamais une source lumineuse","marker.light_entity_label":"Entité lumineuse principale","marker.light_entity.help":"Pour un appareil composite, choisissez l’entité dont l’état, la couleur et la luminosité représentent cette source sur le plan. Le choix automatique reste compatible avec les anciens plans.","marker.light_entity.help.aria":"Aide : entité lumineuse principale","marker.light_entity_auto":"Automatique ({entity})","marker.light_entity_none":"aucune entité contrôlable","marker.light_entity_missing":"L’entité enregistrée {entity} est indisponible. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.toggle_entity_label":"Entité à basculer","marker.toggle_entity.help":"Pour un appareil composite, choisissez précisément la lumière ou l’interrupteur propre à actionner par un toucher. Automatique conserve les règles de cible précédentes et reste indépendant de l’entité lumineuse principale.","marker.toggle_entity.help.aria":"Aide : entité à basculer","marker.toggle_entity_auto":"Automatique ({entity})","marker.toggle_entity_none":"aucune entité propre contrôlable","marker.toggle_entity_missing":"L’entité enregistrée {entity} ne fait plus partie des canaux sélectionnables de ce marqueur. House Plan utilise temporairement {fallback} ; le choix enregistré sera restauré si elle revient.","marker.glow_color_label":"Couleur et luminosité du halo","marker.glow_mode.help":"Utilisez les valeurs en direct de la source, remplacez uniquement sa couleur ou fixez à la fois la couleur et la luminosité. Le minimum est 1 % ; pour désactiver la source, choisissez « Jamais ».","marker.glow_mode.help.aria":"Aide : couleur et luminosité du halo","marker.glow_mode_auto":"Depuis la source","marker.glow_mode_color":"Définir la couleur","marker.glow_mode_fixed":"Définir la couleur et la luminosité","marker.glow_color":"Couleur du halo","marker.glow_brightness":"Luminosité","marker.glow_disabled_never":"Les paramètres du halo sont indisponibles car ce marqueur est explicitement défini comme n’étant pas une source lumineuse.","marker.glow_disabled_auto":"Le mode Auto n’a trouvé aucune source lumineuse spatiale pour ce marqueur.","marker.glow_disabled_no_entity":"Aucune entité Home Assistant contrôlable et active n’est disponible pour cette source spatiale.","marker.glow_passive_hint":"Cette source ne possède aucune donnée Home Assistant propre. Définissez manuellement sa couleur et sa luminosité ; son rayon reste disponible.","marker.control_broken":"La source enregistrée est absente ou n’est plus marquée comme lumière","marker.control_missing_label":"Lumière du plan manquante","marker.control_passive":"source passive","toast.marker_control_cycle":"Ce lien créerait une chaîne circulaire de contrôles de lumière.","toast.marker_binding_required":"Choisissez un appareil Home Assistant avant de lier une autre source lumineuse.","confirm.unlock_title":"Déverrouiller ?","confirm.unlock_body":"House Plan enverra une commande de déverrouillage à cette serrure.","toast.files_migrate_failed":"Les pièces jointes n’ont pas pu être déplacées vers la nouvelle association ; les liens continuent de pointer vers les anciens fichiers : {err}","space.pick_saved":"Déjà importé","space.pick_saved_hint":"Plans stockés sur le serveur, y compris ceux que vous avez précédemment dissociés","space.no_saved":"Aucun plan n’est encore stocké sur le serveur.","space.loading":"Chargement…","space.used_by":"utilisé par : {list}","space.in_use":"Un espace utilise encore ce plan — dissociez-le d’abord","btn.use":"Utiliser","confirm.delete_plan_title":"Supprimer le fichier du plan ?","confirm.delete_plan_body":"Le fichier sera supprimé du serveur. Cette action est irréversible.","toast.plans_list_failed":"Impossible de lister les plans stockés : {err}","toast.plan_delete_failed":"Impossible de supprimer le plan : {err}","marker.hide":"Masquer","marker.hide_tip":"L’appareil disparaîtra du plan après l’enregistrement mais continuera de compter dans le signal de la pièce. Restaurez-le via « Masqués et désactivés » dans l’éditeur d’appareils.","marker.show":"Afficher","marker.show_tip":"L’appareil apparaîtra de nouveau sur le plan après l’enregistrement.","marker.hidden_ghost":"Appareil masqué par l’utilisateur","marker.ha_disabled_device":"L’appareil est désactivé dans Home Assistant et masqué du plan.","marker.ha_disabled_entity":"L’entité est désactivée dans Home Assistant et masquée du plan.","marker.ha_disabled_all_entities":"L’appareil n’a aucune entité Home Assistant active ; il est donc masqué du plan.","marker.ha_registry_limited":"Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.","marker.delete_tip":"Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.","tap.run":"Exécuter une automatisation/un script/une scène","tap.none":"Ne rien faire","marker.run_target_label":"Élément à exécuter","marker.run_search_ph":"Rechercher : automatisation, script ou scène…","marker.run_target_gone":"Cible {id} introuvable — choisissez-en une autre","marker.tap_confirm":"Demander une confirmation","marker.tap_confirm_tip":"Afficher une boîte de dialogue de confirmation avant l’action — protection contre les appuis accidentels.","marker.toggle_hint_single":"Cible : {name} ({id}).","marker.virtual_light_target":"Lumière virtuelle : {name}.","marker.virtual_light_current":"État manuel : {state} → {effect}.","marker.virtual_light_state_on":"allumé","marker.virtual_light_state_off":"éteint","marker.toggle_hint_group":"Basculera {count} source(s) : {names}.","marker.toggle_hint_current":"Actuellement : {state} → {effect}.","marker.toggle_hint_group_current":"Actuellement allumées : {on} sur {count} → {effect}.","marker.toggle_hint_skipped":"Seront ignorées ({count}) : {targets}.","marker.toggle_effect_turn_on":"sera allumé","marker.toggle_effect_turn_off":"sera éteint","marker.toggle_effect_open":"sera ouvert","marker.toggle_effect_close":"sera fermé","marker.toggle_effect_stop":"sera arrêté","marker.toggle_effect_toggle":"l’état sera basculé","marker.toggle_skip_missing":"introuvable","marker.toggle_skip_ha_disabled":"désactivé dans HA","marker.toggle_skip_unavailable":"indisponible","marker.toggle_skip_unsupported":"basculement non pris en charge","marker.toggle_skip_secure":"bloqué pour des raisons de sécurité","marker.toggle_none_no_binding":"Cet appareil n’a aucune association ; il n’y a donc aucun état à basculer. Un toucher ne fera rien.","marker.toggle_none_no_actionable_entity":"Cet appareil n’a aucun état pouvant être basculé. Un toucher ne fera rien.","marker.toggle_none_configured_targets_missing":"Les cibles configurées sont indisponibles. L’entité propre de l’appareil ne leur sera pas substituée.","marker.toggle_none_ha_disabled":"La cible est désactivée dans Home Assistant et ne peut pas être utilisée sur le plan.","marker.toggle_none_unavailable":"La cible est actuellement indisponible. Un toucher ne fera rien.","marker.toggle_none_unsupported":"Home Assistant n’expose aucune opération de basculement sûre pour cette entité. Un toucher ne fera rien.","marker.toggle_none_secure":"Le basculement des serrures, alarmes et portails sécurisés depuis le plan est bloqué pour des raisons de sécurité.","run.automation":"automatisation","run.script":"script","run.scene":"scène","confirm.tap_run":"Exécuter « {name} » ?","confirm.tap_toggle":"Basculer « {name} » ?","confirm.current_state":"État actuel : {state}","confirm.expected_state":"Après basculement : {state}","confirm.group_current":"{on} allumé(s) sur {total}","confirm.group_all_on":"tout est allumé","confirm.group_all_off":"tout est éteint","confirm.unavailable_targets":"Indisponibles : {count}","confirm.expected_by_ha":"Home Assistant déterminera l’état","confirm.state_on":"Allumé","confirm.state_off":"Éteint","confirm.state_open":"Ouvert","confirm.state_closed":"Fermé","confirm.state_opening":"Ouverture en cours","confirm.state_closing":"Fermeture en cours","confirm.state_stopped":"Arrêté","confirm.state_unknown":"Inconnu","toast.run_started":"Démarré : {name}","toast.run_target_missing":"Cible d’exécution introuvable — vérifiez les paramètres de l’appareil","toast.run_target_required":"Choisissez une automatisation, un script ou une scène","toast.tap_target_changed":"La cible de l’action a changé. Réessayez.","toast.virtual_light_toggle_failed":"Impossible de basculer la lumière virtuelle : {err}","toast.value_badge_source_required":"Choisissez une valeur pour le badge","btn.run":"Exécuter","vac.section":"Robot aspirateur : position en direct","vac.autocal":"Configurer automatiquement","vac.live":"Position en direct sur le plan","vac.trail":"Afficher le trajet du robot","vac.autocal_no_rooms":"L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »","vac.route_warn_unmapped":"Le robot nettoie une carte qui n'est assignée à aucun étage","vac.route_warn_needs_calibration":"La carte est assignée à un étage mais n'est pas calibrée","vac.route_warn_ambiguous":"Deux cartes sont indiscernables — aucun étage choisi","vac.route_warn_missing_space":"L'espace de cette carte a été supprimé","vac.autocal_no_match":"Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »","vac.autocal_done":"Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez","vac.cal_need_pos":"Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause","vac.cal_done":"Calibration enregistrée. Lancez un nettoyage et vérifiez","vac.cal_cancelled":"Calibration annulée","vac.fit":"Ajuster manuellement","vac.fit_hint":"Faites glisser la carte du robot pour la positionner, étirez-la par les coins","vac.fit_rotate":"Rotation de 90°","vac.fit_mirror":"Miroir","vac.trail_never":"Jamais","vac.trail_cleaning":"Pendant le nettoyage","vac.trail_always":"Toujours","gs.bg_group":"Arrière-plan de la scène","gs.bg_color":"Arrière-plan autour du plan","gs.bg_default":"Valeur par défaut du thème","gs.bg_theme":"valeur par défaut du thème","gs.bg_mode":"Arrière-plan du plan","gs.bg_mode.help":"« Suit le soleil » utilise sun.sun ou, à défaut, l’heure locale du navigateur ; « Couleur statique » affiche toujours la couleur choisie.","gs.bg_mode.help.aria":"Aide : arrière-plan général du plan","gs.bg_static":"Couleur statique","gs.bg_daynight":"Suit le soleil","gs.sun_group":"Soleil","gs.sun_missing":"L’entité sun.sun est introuvable — l’arrière-plan suit votre heure locale ; les rayons de soleil par les fenêtres sont indisponibles.","gs.north":"Nord sur le plan","gs.north.help":"L’angle se mesure dans le sens horaire depuis la verticale supérieure du plan ; le nord sert aux rayons des fenêtres, pas à l’arrière-plan « Suit le soleil ».","gs.north.help.aria":"Aide : direction générale du nord","gs.north_ph":"non défini","gs.north_clear":"Effacer","gs.north_letter":"N","gs.sun_rays":"Lumière du soleil à travers les fenêtres","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · documentation et problèmes","gs.about_telegram":"Discussion Telegram","support.title":"Aide et commentaires","space.bg_color":"Arrière-plan autour du plan","space.bg_inherit":"Hériter des paramètres généraux","space.bg_inherited":"hérite des paramètres généraux","space.bg_mode":"Arrière-plan du plan","space.bg_mode.help":"Héritez de l’arrière-plan général ou remplacez-le pour cet espace par une couleur statique ou le mode « Suit le soleil ».","space.bg_mode.help.aria":"Aide : arrière-plan de l’espace","space.north":"Nord sur le plan (remplacement)","space.north.help":"Remplace le nord général pour cet espace ; l’angle se mesure dans le sens horaire depuis la verticale supérieure du plan et sert aux rayons des fenêtres.","space.north.help.aria":"Aide : nord de cet espace","space.north_inherited":"hérité : {v}","space.sun_rays":"Lumière du soleil à travers les fenêtres","space.sun_inherit":"Hériter des paramètres généraux","space.sun_on":"Activé","space.sun_off":"Désactivé","canvas.far_objects":"{n} objet(s) éloigné(s) du plan","canvas.show_far":"Afficher","canvas.home_tip":"Le plan est là-bas — cliquez pour l’ajuster à l’écran","gs.grid_group":"Maintenance du plan","gs.grid_hint":"Met à jour les modèles de données, aligne les éléments du plan sur la grille et fusionne les fragments de murs redondants. Un rapport précis est affiché avant tout enregistrement.","gs.align_all":"Optimiser les plans","gs.align_title":"Optimiser les plans","gs.align_none":"Tous les plans utilisent déjà le modèle de données optimisé actuel.","gs.optimize_no_automatic_changes":"Aucune modification automatique n’est à appliquer. Vérifiez les éléments ci-dessous.","gs.align_count":"{n} élément(s) sur {total} seront déplacés, de {cm} cm au maximum.","gs.align_where":"Le plus grand déplacement se trouve dans « {s} ».","gs.align_turned":"Ouvertures dont l’angle est corrigé : {n}.","gs.align_removed_drafts":"Contours invalides réduits par la grille et supprimés : {n}.","gs.optimize_redundant_drafts":"Chaînes de murs enregistrées masquées par des murs de pièces pleins et supprimées : {n}.","gs.align_preflight_failed":"Impossible de vérifier en toute sécurité la géométrie des espaces suivants : {spaces}{more}.","gs.align_preflight_hint":"Les plans n’ont pas été modifiés. Copiez les diagnostics avec le bouton ci-dessous et joignez-les au rapport de bug avec un export de l’espace.","gs.preflight_reason_prepare-exception":"Impossible de préparer la géométrie de l’espace (exception lors de la construction du modèle)","gs.preflight_reason_wall-null":"Le corps du mur n’a pas pu être construit (l’union a renvoyé un résultat vide)","gs.preflight_reason_wall-degraded-extra":"Le corps du mur s’est dégradé avec une géométrie supplémentaire","gs.preflight_reason_wall-failed-core":"Le noyau du corps du mur n’a pas pu être assemblé","gs.preflight_reason_wall-exception":"La construction du mur a déclenché une exception","gs.preflight_reason_floor-null":"Le contour du sol n’a pas pu être construit","gs.preflight_reason_floor-exception":"La construction du sol a déclenché une exception","gs.preflight_copy":"Copier les diagnostics","gs.preflight_copied":"Diagnostics copiés","gs.preflight_update_hint":"Les versions de la carte et de l’intégration diffèrent — mettez House Plan à jour puis réessayez.","gs.align_preflight_space":"Espace {n}","gs.align_preflight_more":", et {n} de plus","gs.optimize_changes":"Migrations de modèle : {m} ; espaces mis à jour : {c} ; valeurs de coordonnées parasites supprimées : {p} ; fragments de murs réels fusionnés : {w} ; fragments de murs d’épaisseur nulle fusionnés : {s} ; murs indépendants : {i}.","gs.zero_walls_migrated":"Portions de murs virtuels converties : {n}.","gs.wall_segments_migrated":"Segments de murs stabilisés : {n}.","gs.optimize_lattice_summary":"Valeurs de coordonnées parasites normalisées : {n} ; déplacement maximal : {cm} cm.","gs.optimize_lattice_space":"{space} : valeurs de coordonnées normalisées : {n} ; valeurs hors grille laissées inchangées : {far}.","gs.optimize_coincident_partitions":"Sections de murs indépendants masquées absorbées dans les murs des pièces : {n}.","gs.optimize_openings_rehosted":"Ouvertures rattachées aux murs des pièces : {n}.","gs.optimize_walls_straightened":"Murs redressés : {n} ; déplacement maximal : {cm} cm.","gs.optimize_walls_straightened_where":"Plus grande correction de mur : {s}.","gs.optimize_walls_straighten_skipped":"Murs proches d’un axe laissés inchangés car ils ne pouvaient pas être réparés en toute sécurité : {n}.","gs.optimize_glow_migration":"Ancien mode Glow : {spaces} espaces → aucun remplissage de données + Glow indépendant ; {rooms} pièces → remplissage de données hérité + Glow indépendant.","gs.optimize_references":"Références réparées : espaces — {spaces} ; pièces — {rooms} ; positions — {positions} ; appareils dissociés des espaces manquants — {detached}.","gs.optimize_reference_more":", et {n} de plus","gs.optimize_orphans_removed":"Enregistrements oubliés supprimés : {total} — étiquettes de pièces : {rooms} ; appareils : {devices} ; marqueurs de groupe : {groups}. Ils appartenaient à des espaces supprimés précédemment.","gs.optimize_live_positions":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles seront conservées.","gs.optimize_live_positions_remove":"Anciennes positions dans des espaces supprimés appartenant à des objets existants : {n}{names}. Elles sont sélectionnées pour suppression.","gs.optimize_live_names":" : {names}{more}","gs.optimize_live_remove":"Supprimer les anciennes positions","gs.optimize_live_keep":"Conserver les anciennes positions","gs.optimize_live_selected":"Les anciennes positions seront supprimées après l’application de l’optimisation.","gs.optimize_unverified":"Impossible de vérifier en toute sécurité les positions : {n}. Elles ont été laissées inchangées.","gs.optimize_registry_limited":"Un accès administrateur complet aux registres Home Assistant est requis pour une vérification sûre.","gs.optimize_vacuum_warning":"Associations de pièces d’aspirateur nécessitant encore une vérification : {n}.","gs.optimize_details":"Détails","gs.optimize_details_more":"Et {n} enregistrement(s) de plus.","gs.optimize_detail_removed":"sera supprimé","gs.optimize_detail_live":"sera conservé","gs.optimize_detail_unverified":"non vérifié","gs.optimize_detail_room_label":"étiquette de pièce","gs.optimize_detail_device":"appareil","gs.optimize_detail_group":"marqueur de groupe","gs.optimize_detail_unknown":"propriétaire inconnu","gs.optimize_detail_item":"{status} : {kind} {id} ; ancien espace {space}","gs.align_warn":"Les éléments volontairement placés entre les nœuds de la grille seront déplacés. Une seule annulation est disponible après l’opération, uniquement jusqu’à la prochaine modification du plan.","gs.align_run":"Optimiser","gs.align_done":"Plans optimisés : {n} élément(s) déplacé(s), {m} enregistrement(s) maintenu(s), {r} référence(s) réparée(s)","gs.optimize_undo":"Annuler la dernière optimisation","gs.optimize_undone":"La dernière optimisation a été annulée","decor.furniture":"Mobilier","decor.image":"Image","decor.image_title":"Images personnalisées","decor.image_upload":"Importer une image","decor.image_replace":"Remplacer l’image","decor.image_uploading":"Importation…","decor.image_asset":"Fichier image","decor.image_pick_hint":"Importez une image ou choisissez-en une existante.","decor.image_place_hint":"Cliquez sur le plan pour placer l’image.","decor.image_none":"Aucune image importée pour le moment.","decor.image_unavailable":"Image indisponible","decor.image_used":"Utilisée dans {n} objets","decor.image_upload_failed":"Impossible d’importer l’image : {err}","decor.image_error_capacity":"Le stockage des images personnalisées est plein.","decor.image_in_use":"Supprimez toutes les copies placées avant ce fichier.","decor.image_delete_title":"Supprimer le fichier image ?","decor.image_delete_message":"Supprimer « {name} » de House Plan ? Cette action est irréversible.","furn.title":"Bibliothèque de mobilier","furn.symbol":"Symbole","furn.group_furniture":"Mobilier","furn.group_appliance":"Électroménager","furn.group_sanitary":"Sanitaires","furn.group_other":"Autre","furn.width":"Largeur","furn.depth":"Profondeur","furn.back_to_categories":"Toutes les catégories","furn.pick_hint":"Choisissez un symbole, puis cliquez sur le plan.","furn.place_hint":"Cliquez sur le plan — le meuble se place contre le mur le plus proche. Maj permet un placement libre.","furn.cat_air_conditioner":"Climatiseurs","furn.cat_armchair":"Fauteuils","furn.cat_bathtub":"Baignoires","furn.cat_bed":"Lits","furn.cat_bidet":"Bidets","furn.cat_boiler":"Chauffe-eau","furn.cat_chair":"Chaises","furn.cat_coffee_table":"Tables basses","furn.cat_cooktop":"Plaques de cuisson","furn.cat_dining_table":"Tables à manger","furn.cat_dishwasher":"Lave-vaisselle","furn.cat_dryer":"Sèche-linge","furn.cat_fireplace":"Cheminées","furn.cat_fridge":"Réfrigérateurs","furn.cat_kitchen_cabinet":"Meubles de cuisine","furn.cat_kitchen_sink":"Éviers de cuisine","furn.cat_nightstand":"Meubles","furn.cat_plant":"Plantes","furn.cat_rug":"Tapis","furn.cat_shelving":"Étagères","furn.cat_shower":"Douches","furn.cat_sink":"Lavabos","furn.cat_sofa":"Canapés","furn.cat_stairs":"Escaliers","furn.cat_toilet":"Toilettes","furn.cat_tv":"Téléviseurs","furn.cat_wardrobe":"Armoires","furn.cat_washer":"Lave-linge","furn.cat_work_table":"Bureaux","furn.sym_sofa":"Canapé","furn.sym_sofa_three_seat":"Canapé trois places","furn.sym_sofa_corner_right":"Canapé d'angle droit","furn.sym_armchair":"Fauteuil","furn.sym_armchair_office":"Chaise de bureau","furn.sym_coffee_table":"Table basse","furn.sym_coffee_table_round":"Table basse ronde","furn.sym_coffee_table_oval":"Table basse ovale","furn.sym_coffee_table_rounded":"Table basse arrondie","furn.sym_table_dining":"Table à manger","furn.sym_table_round":"Table ronde","furn.sym_table_dining_oval":"Table à manger ovale","furn.sym_table_dining_rounded":"Table à manger arrondie","furn.sym_chair":"Chaise","furn.sym_chair_bar":"Tabouret de bar","furn.sym_desk":"Bureau","furn.sym_desk_corner":"Bureau d'angle","furn.sym_bed_double":"Lit double","furn.sym_bed_single":"Lit simple","furn.sym_nightstand":"Table de chevet","furn.sym_cabinet_tv":"Meuble TV","furn.sym_cabinet_shoe":"Meuble à chaussures","furn.sym_cabinet_sink":"Meuble sous lavabo","furn.sym_wardrobe":"Armoire","furn.sym_bookshelf":"Bibliothèque","furn.sym_wall_unit":"Élément mural","furn.sym_kitchen_floor":"Module de cuisine bas","furn.sym_kitchen_floor_corner":"Module de cuisine bas d'angle","furn.sym_kitchen_wall":"Module de cuisine haut","furn.sym_kitchen_wall_corner":"Module de cuisine haut d'angle","furn.sym_shelf_floor":"Étagère au sol","furn.sym_shelf_wall":"Étagère murale","furn.sym_fridge":"Réfrigérateur","furn.sym_stove":"Cuisinière","furn.sym_cooktop_two":"Plaque à deux feux","furn.sym_dishwasher":"Lave-vaisselle","furn.sym_washer":"Lave-linge","furn.sym_dryer":"Sèche-linge","furn.sym_tv":"Téléviseur","furn.sym_tv_wall":"Téléviseur mural","furn.sym_ac":"Climatiseur","furn.sym_water_heater":"Chauffe-eau","furn.sym_toilet":"Toilettes","furn.sym_toilet_built_in":"Toilettes suspendues","furn.sym_bathtub":"Baignoire","furn.sym_bathtub_corner":"Baignoire d'angle","furn.sym_shower":"Douche","furn.sym_sink":"Lavabo","furn.sym_kitchen_sink":"Évier de cuisine","furn.sym_kitchen_sink_double":"Évier de cuisine double","furn.sym_bidet":"Bidet","furn.sym_bidet_built_in":"Bidet suspendu","furn.sym_stairs":"Escalier","furn.sym_fireplace":"Cheminée","furn.sym_plant":"Plante","furn.sym_rug":"Tapis","common.yes":"Oui","common.no":"Non","vac.diag_source":"Source","vac.diag_platform":"Intégration","vac.diag_status":"État","vac.diag_position":"Position","vac.diag_rooms":"Pièces","vac.diag_rooms_value":"{total} · {matched} noms correspondent · {readiness}","vac.autocal_ready":"calibration automatique disponible","vac.autocal_not_ready":"3 noms correspondants requis","vac.diag_path":"Chemin de l’intégration","vac.diag_map":"ID de carte","vac.source_none":"non sélectionné","vac.source_status_ok":"Prêt","vac.source_status_missing":"Manquant","vac.source_status_disabled":"Désactivé dans Home Assistant","vac.source_status_unavailable":"Indisponible","vac.source_status_unverified":"Impossible à vérifier avec les autorisations actuelles","vac.source_status_unsupported":"Aucune donnée de position","vac.source_status_none":"Aucune source","vac.source_banner_missing":"La source enregistrée n’existe plus. Elle n’a pas été remplacée automatiquement ; choisissez une autre source ou restaurez-la dans Home Assistant.","vac.source_banner_disabled":"La source enregistrée est désactivée dans Home Assistant. Activez-la ou choisissez une autre source.","vac.source_banner_unverified":"Les autorisations Home Assistant actuelles ne permettent pas de vérifier cette source enregistrée. Elle reste épinglée et ne sera pas remplacée automatiquement.","vac.choose_source":"Choisir la source","vac.source_auto":"Automatique","vac.source_auto_hint":"Utiliser une entité compatible de cet appareil","vac.all_cameras":"Toutes les caméras","vac.all_cameras_warn":"Une caméra peut ne pas fournir de données du robot. Choisissez-en une uniquement s’il s’agit de la carte de votre aspirateur.","vac.all_cameras_empty":"Aucune autre entité caméra trouvée.","vac.platform_unknown":"intégration inconnue","vac.cap_position":"position","vac.cap_rooms_short":"pièces","vac.cap_path":"trajet","vac.cap_map":"ID de carte","vac.cap_none":"aucune donnée de robot détectée","vac.xcme_hint":"Activez ces attributs Xiaomi Cloud Map Extractor :","vac.documentation":"Documentation","vac.residual_title":"Vérifier la calibration automatique","vac.residual_message":"Les pièces correspondantes présentent un écart maximal de {error}. Appliquez cette calibration approximative, affinez-la manuellement ou annulez sans modifier la configuration enregistrée.","vac.apply_proposal":"Appliquer","gs.backup_group":"Sauvegarde et transfert","gs.backup_hint":"Téléchargez une sauvegarde JSON portable ou prévisualisez et importez une sauvegarde créée par House Plan.","backup.export_open":"Exporter","backup.import_open":"Importer","backup.export_title":"Exporter House Plan","backup.import_title":"Importer House Plan","backup.export_hint":"Choisissez si la sauvegarde doit contenir toute la configuration House Plan ou uniquement l’espace actuel.","backup.full":"Sauvegarde complète","backup.current_space":"Espace actuel","backup.current_space_title":"Espace actuel : {title}","backup.no_current_space":"Aucun espace actuel","backup.plan_only":"Plan uniquement","backup.plan_only_hint":"Conserver les pièces, murs, ouvertures, décorations et positions des étiquettes de pièces sans les appareils ni les associations Home Assistant.","backup.plan_only_preview":"Ce fichier contient uniquement le plan","backup.privacy_warning":"L’archive conserve les noms, identifiants Home Assistant et coordonnées exactes. Les plans internes et pièces jointes sont référencés, pas intégrés ; les états d’exécution et trajets d’aspirateur ne sont pas inclus.","backup.download":"Télécharger le JSON","backup.export_done":"Sauvegarde téléchargée","backup.reading":"Vérification de la sauvegarde…","backup.revalidated":"Le plan a changé depuis cet aperçu. Le résumé a été actualisé ; vérifiez-le et confirmez de nouveau.","backup.error.support_invalid_message":"Vérifiez le message d’assistance et réessayez.","backup.error.support_package_too_large":"Le paquet d’assistance anonymisé dépasse la limite de 8 Mio.","backup.error.support_preview_expired":"L’aperçu de la pièce jointe d’assistance a expiré ou n’est plus disponible.","backup.error.support_rate_limited":"Trop de rapports ou d’aperçus d’assistance ont été demandés. Réessayez plus tard.","backup.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","backup.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible.","backup.error.unauthorized":"Vous n’avez pas l’autorisation d’exporter ou d’importer ce plan.","backup.error.not_ready":"House Plan n’est pas encore prêt. Réessayez dans un instant.","backup.error.too_large":"La sauvegarde dépasse la limite de 8 Mio.","backup.error.invalid_json":"Le fichier sélectionné n’est pas un JSON valide.","backup.error.invalid_format":"Le fichier sélectionné n’est pas une sauvegarde House Plan.","backup.error.invalid_image":"L’image est endommagée ou ne correspond pas à son format.","backup.error.unsupported_export_version":"Ce format de sauvegarde n’est pas pris en charge par la version installée.","backup.error.unsupported_image":"Utilisez une image PNG, JPEG, WebP ou SVG prise en charge.","backup.error.future_model":"La sauvegarde a été créée avec un modèle de données House Plan plus récent.","backup.error.invalid_config":"La sauvegarde contient une configuration House Plan invalide.","backup.error.wall_model_migration_blocked":"Le modèle de murs de la sauvegarde n’a pas pu être mis à niveau en toute sécurité. Optimisez d’abord le plan source, puis exportez-le de nouveau.","backup.error.invalid_layout":"La sauvegarde contient des positions d’objets invalides.","backup.error.invalid_content":"La sauvegarde contient des références de contenu invalides ou incohérentes.","backup.error.space_not_found":"L’espace sélectionné n’existe plus.","backup.error.capacity_exceeded":"L’ajout de cette sauvegarde dépasserait les limites du plan.","backup.error.preview_expired":"L’aperçu a expiré. Sélectionnez de nouveau le fichier de sauvegarde.","backup.error.preview_owner_mismatch":"Cet aperçu appartient à un autre utilisateur Home Assistant.","backup.error.conflict":"Le plan a changé depuis l’aperçu. Vérifiez le résumé actualisé.","backup.error.content_confirmation_required":"Confirmez que le contenu local indisponible peut être dissocié.","backup.error.commit_failed":"La sauvegarde n’a pas pu être appliquée en toute sécurité. Le plan précédent a été restauré ou est en attente de récupération.","backup.error.missing_plan":"Un fichier de plan référencé a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.missing_content":"Une pièce jointe locale référencée a disparu depuis l’aperçu. Vérifiez de nouveau la sauvegarde.","backup.error.marker_control_missing":"Une lumière liée au plan est absente de la sauvegarde.","backup.error.marker_control_not_light":"Une cible liée au plan n’est plus marquée comme source lumineuse.","backup.error.marker_control_self":"Une source lumineuse ne peut pas se contrôler elle-même.","backup.error.marker_control_cycle":"La sauvegarde contient une chaîne circulaire de contrôles de lumière.","backup.error.duplicate_marker_control":"La sauvegarde contient un lien de lumière du plan en doublon.","backup.error.no_backup":"Aucun instantané d’importation ou d’optimisation n’est disponible à restaurer.","backup.error.in_use":"L'élément est encore utilisé","backup.error.invalid_data":"La requête contient des données invalides","backup.error.invalid_light_entity":"La lumière principale doit être une entité light.* ou switch.*","backup.error.invalid_marker_control":"Référence de contrôle de marqueur invalide","backup.error.invalid_name":"Le nom est invalide","backup.error.invalid_partition_opening_host":"L'ouverture doit conserver sa cloison porteuse","backup.error.invalid_partition_opening_jamb_margin":"L'ouverture ne laisse aucun jambage physique au bout du mur","backup.error.invalid_passage_fields":"Un passage ouvert ne peut pas porter de réglages de porte","backup.error.invalid_space_id":"Espace inconnu","backup.error.invalid_toggle_entity":"L'entité de bascule doit être light.* ou switch.*","backup.error.invalid_value_badge":"Réglages de badge de valeur invalides","backup.error.invalid_value_badge_attribute":"Attribut de badge de valeur invalide","backup.error.invalid_value_badge_position":"Position de badge de valeur invalide","backup.error.invalid_value_badge_source":"Source de badge de valeur invalide","backup.error.invalid_vacuum_map_route":"Vérifiez l'affectation des cartes du robot aux espaces.","backup.error.invalid_value_source":"Source de valeur invalide","backup.error.invalid_value_source_attribute":"Attribut de source de valeur invalide","backup.error.io_error":"Une opération de fichier a échoué sur le serveur","backup.error.not_toggleable":"Cet appareil ne peut pas être basculé","backup.error.nothing_to_repair":"Rien à réparer","backup.error.space_in_use":"L'espace est encore référencé","backup.error.value_badge_source_required":"Le badge de valeur nécessite une source","backup.error.wall_model_client_outdated":"Le plan a été mis à jour ailleurs — rechargez la page","backup.same_source":"Créée sur cette instance Home Assistant","backup.foreign_source":"Créée sur une autre instance Home Assistant","backup.created":"Créée : {value}","backup.versions":"Carte {card} ; intégration {integration} ; modèle de données {model}","backup.count_spaces":"Espaces : {n}","backup.count_rooms":"Pièces : {n}","backup.count_walls":"Murs : {n}","backup.count_openings":"Ouvertures : {n}","backup.count_decor":"Objets décoratifs : {n}","backup.count_markers":"Appareils : {n}","backup.count_layout":"Positions : {n}","backup.bindings":"Associations — appareils : {device}, entités : {entity}, virtuels : {virtual} ; positions sans espace : {legacy}","backup.binding_status":"État des cibles — actives : {active}, désactivées : {disabled}, manquantes : {missing}","backup.missing_areas":"Zones absentes sur la cible : {areas}","backup.dropped_marker_links":"Liens de lumières du plan hors de cet espace transféré omis : {n}.","backup.repaired_target_refs":"Références existantes restaurées par cet import d’espace : {n}.","backup.preserved_unresolved_refs":"Références qui n’ont pas pu être restaurées sans ambiguïté conservées : {n}.","backup.preserved_unresolved_hint":"Aucune donnée n’a été devinée ni supprimée. Après l’importation, lancez « Optimiser les plans » pour examiner les références restantes.","backup.import_details":"Détails des références d’importation","backup.import_detail.incoming_remapped":"Références mises à jour dans la copie importée : {n}","backup.import_detail.target_repaired":"Références existantes restaurées : {n}","backup.import_detail.preserved_unresolved":"Références non résolues conservées : {n}","backup.import_detail.collisions":"Conflits de destination conservés en toute sécurité : {n}","backup.import_detail.dropped_links":"Liens entrants omis selon les règles de transfert : {n}","backup.import_detail.bounded_lineages":"Identifiants trop imbriqués maintenus dans les limites : {n}","backup.replace_warning":"Cela remplace la configuration et la disposition actuelles. Les fichiers importés ne sont jamais supprimés. Une seule annulation reste disponible jusqu’à la prochaine modification du plan.","backup.foreign_bookkeeping":"Les données de suivi des appareils connus/nouveaux propres à l’instance ne seront pas importées.","backup.final_name":"Nom du nouvel espace","backup.target_settings":"Les paramètres généraux de l’instance cible restent inchangés.","backup.duplicates":"Associations déjà présentes : {n}","backup.skip":"Ignorer les appareils en doublon","backup.virtual_copy":"Ajouter des copies virtuelles statiques sûres","backup.content":"Contenu référencé","backup.decor_images_summary":"Images personnalisées : {objects} objets, {assets} fichiers ; fichiers absents : {missing}.","backup.content_available":"disponible localement","backup.content_external":"lien externe","backup.content_detach_required":"sera dissocié","backup.confirm_detach":"Je comprends que les plans internes et pièces jointes indisponibles seront dissociés de la configuration importée.","backup.confirm_missing_images":"Je comprends que les images personnalisées absentes resteront des espaces réservés réparables et que les autres fichiers locaux indisponibles seront dissociés.","backup.replace":"Remplacer","backup.add":"Ajouter un espace","backup.space_done":"Espace importé : {rooms} pièces, {markers} appareils, {refs} références existantes restaurées","backup.full_done":"Sauvegarde restaurée : {spaces} espaces, {rooms} pièces, {markers} appareils","backup.undo_import":"Annuler la dernière importation complète","backup.import_undone":"Importation complète annulée","backdrop.large_title":"Grande image","backdrop.large_body":"Cette image fait {w}×{h} (fichier de {fileMb} Mo) et nécessite environ {decodedMb} Mo de mémoire pour s'afficher. Sur tablette, cela peut faire planter la page.","backdrop.unknown_body":"Les dimensions de l'image n'ont pas pu être lues — le fichier est peut-être endommagé. Continuer peut faire planter la page sur tablette.","backdrop.reduced_dimensions":"Copie réduite : {w}×{h} px.","backdrop.use_downscaled":"Téléverser une copie réduite","backdrop.reducing":"Réduction…","backdrop.keep_original":"Conserver l'original","backdrop.too_large_title":"Image trop grande","backdrop.too_large_body":"Cette image dépasse ce que les navigateurs peuvent afficher ({w}×{h}, limite {limit} px par côté). Réduisez-la dans un éditeur de bureau puis téléversez-la à nouveau.","backdrop.downscale_failed":"Impossible de créer la copie réduite. Le plan actuel n'a pas été modifié."};const r="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";export{e as dictionary,r as fingerprint}; diff --git a/dist/houseplan-assets/houseplan-card-DcH_msOh.js b/dist/houseplan-assets/houseplan-card-Csq_DR2Y.js similarity index 63% rename from dist/houseplan-assets/houseplan-card-DcH_msOh.js rename to dist/houseplan-assets/houseplan-card-Csq_DR2Y.js index 47d30e8e..35ab8836 100644 --- a/dist/houseplan-assets/houseplan-card-DcH_msOh.js +++ b/dist/houseplan-assets/houseplan-card-Csq_DR2Y.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,R="?"+C,T=`<${R}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,F="[ \t\n\f\r]",O=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,N=RegExp(`>|${F}(?:([^\\s"'>=/]+)(${F}*=${F}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=O;for(let t=0;t"===l[0]?(s=r??O,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?N:'"'===l[3]?L:H):s===L||s===H?s=N:s===I||s===E?s=O:(s=N,r=void 0);const d=s===N&&e[t+1].startsWith("/>")?" ":"";o+=s===O?i+T:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=$?$.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.alert=!1,this.describedBy="",this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._usesHaDialog()?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._usesHaDialog())return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}_usesHaDialog(){return this._useHaDialog&&!this.alert}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._usesHaDialog()){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}rejectClose(){this._closing=!1,this.requestUpdate()}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=W` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),n=new WeakMap;let r=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o;const i=this.t;if(t&&void 0===e){const t=void 0!==i&&1===i.length;t&&(e=n.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&n.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const n=1===e.length?e[0]:t.reduce((t,i,n)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[n+1],e[0]);return new r(n,e,i)},s=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new r("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,m=p.trustedTypes,_=m?m.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?_:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},y=(e,t)=>!a(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=b){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){const i=Symbol(),n=this.getPropertyDescriptor(e,i,t);void 0!==n&&l(this.prototype,e,n)}}static getPropertyDescriptor(e,t,i){const{get:n,set:r}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:n,set(t){const o=n?.call(this);r?.call(this,t),this.requestUpdate(e,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const e=u(this);e.finalize(),void 0!==e.l&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const e=this.properties,t=[...h(e),...d(e)];for(const i of t)this.createProperty(i,e[i])}const e=this[Symbol.metadata];if(null!==e){const t=litPropertyMetadata.get(e);if(void 0!==t)for(const[e,i]of t)this.elementProperties.set(e,i)}this._$Eh=new Map;for(const[e,t]of this.elementProperties){const i=this._$Eu(e,t);void 0!==i&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(s(e))}else void 0!==e&&t.push(s(e));return t}static _$Eu(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),void 0!==this.renderRoot&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){const e=new Map,t=this.constructor.elementProperties;for(const i of t.keys())this.hasOwnProperty(i)&&(e.set(i,this[i]),delete this[i]);e.size>0&&(this._$Ep=e)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,n)=>{if(t)i.adoptedStyleSheets=n.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of n){const n=document.createElement("style"),r=e.litNonce;void 0!==r&&n.setAttribute("nonce",r),n.textContent=t.cssText,i.appendChild(n)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ET(e,t){const i=this.constructor.elementProperties.get(e),n=this.constructor._$Eu(e,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$Em=null}}_$AK(e,t){const i=this.constructor,n=i._$Eh.get(e);if(void 0!==n&&this._$Em!==n){const e=i.getPropertyOptions(n),r="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=n;const o=r.fromAttribute(t,e.type);this[n]=o??this._$Ej?.get(n)??o,this._$Em=null}}requestUpdate(e,t,i,n=!1,r){if(void 0!==e){const o=this.constructor;if(!1===n&&(r=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(r,t)||i.useDefault&&i.reflect&&r===this._$Ej?.get(e)&&!this.hasAttribute(o._$Eu(e,i))))return;this.C(e,t,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:i,reflect:n,wrapped:r},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==r||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===n&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}const e=this.constructor.elementProperties;if(e.size>0)for(const[t,i]of e){const{wrapped:e}=i,n=this[t];!0!==e||this._$AL.has(t)||void 0===n||this.C(t,void 0,i,n)}}let e=!1;const t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=e=>e,S=k.trustedTypes,$=S?S.createPolicy("lit-html",{createHTML:e=>e}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,R="?"+C,T=`<${R}>`,D=document,z=()=>D.createComment(""),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,P=Array.isArray,F="[ \t\n\f\r]",O=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,E=/>/g,N=RegExp(`>|${F}(?:([^\\s"'>=/]+)(${F}*=${F}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=e=>(t,...i)=>({_$litType$:e,strings:t,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),V=Symbol.for("lit-nothing"),G=new WeakMap,K=D.createTreeWalker(D,129);function Y(e,t){if(!P(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==$?$.createHTML(t):t}const X=(e,t)=>{const i=e.length-1,n=[];let r,o=2===t?"":3===t?"":"",s=O;for(let t=0;t"===l[0]?(s=r??O,c=-1):void 0===l[1]?c=-2:(c=s.lastIndex-l[2].length,a=l[1],s=void 0===l[3]?N:'"'===l[3]?L:H):s===L||s===H?s=N:s===I||s===E?s=O:(s=N,r=void 0);const d=s===N&&e[t+1].startsWith("/>")?" ":"";o+=s===O?i+T:c>=0?(n.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?t:d)}return[Y(e,o+(e[i]||"")+(2===t?"":3===t?"":"")),n]};class Z{constructor({strings:e,_$litType$:t},i){let n;this.parts=[];let r=0,o=0;const s=e.length-1,a=this.parts,[l,c]=X(e,t);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(n=K.nextNode())&&a.length0){n.textContent=S?S.emptyScript:"";for(let i=0;iP(e)||"function"==typeof e?.[Symbol.iterator])(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==V&&A(this._$AH)?this._$AA.nextSibling.data=e:this.T(D.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,n="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===n)this._$AH.p(t);else{const e=new Q(n,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=G.get(e.strings);return void 0===t&&G.set(e.strings,t=new Z(e)),t}k(e){P(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,n=0;for(const r of e)n===t.length?t.push(i=new ee(this.O(z()),this.O(z()),this,this.options)):i=t[n],i._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=V}_$AI(e,t=this,i,n){const r=this.strings;let o=!1;if(void 0===r)e=J(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const n=e;let s,a;for(e=r[0],s=0;s{const n=i?.renderBefore??t;let r=n._$litPart$;if(void 0===r){const e=i?.renderBefore??null;n._$litPart$=r=new ee(t.insertBefore(z(),e),e,void 0,i??{})}return r._$AI(e),r},ce=globalThis;let he=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=le(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};he._$litElement$=!0,he.finalized=!0,ce.litElementHydrateSupport?.({LitElement:he});const de=ce.litElementPolyfillSupport;de?.({LitElement:he}),(ce.litElementVersions??=[]).push("4.2.2");const ue=new WeakMap;let pe=0;class me extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.alert=!1,this.describedBy="",this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++pe,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._usesHaDialog()?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=e=>{if("Escape"===e.key){e.preventDefault(),e.stopImmediatePropagation(),this._pruneOverlays();const t=this._overlays[this._overlays.length-1];return t?void this._closeOverlay(t,"escape"):void this._requestClose()}if("Tab"!==e.key||this._usesHaDialog())return;const t=this.renderRoot.querySelector(".close"),i=t?[t,...this._focusableElements()]:this._focusableElements();if(!i.length)return e.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const n=this._deepActiveElement(),r=i[0],o=i[i.length-1];!e.shiftKey||n!==r&&i.includes(n)?e.shiftKey||n!==o||(e.preventDefault(),r.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}_usesHaDialog(){return this._useHaDialog&&!this.alert}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=ue.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),ue.set(e,t),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const e=[...this._overlays];this._overlays=[];for(const t of e.reverse())t.close("disconnect");const t=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const n=t?ue.get(t):void 0;n?.dialogs.delete(this),super.disconnectedCallback(),t&&n&&requestAnimationFrame(()=>{const e=ue.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void ue.delete(t);const n=i?.closest("hp-dialog");i?.isConnected&&n&&e.dialogs.has(n)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._usesHaDialog()){const e=this.renderRoot.querySelector("dialog");e&&!e.open&&e.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let e=document.activeElement;for(;e?.shadowRoot?.activeElement;)e=e.shadowRoot.activeElement;return e&&e!==document.body?e:null}_focusableElements(){const e=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),t=[],i=new Set,n=r=>{if(!i.has(r)){if(i.add(r),r instanceof HTMLElement){if(r.matches(e)&&t.push(r),r instanceof HTMLSlotElement){for(const e of r.assignedNodes({flatten:!0}))n(e);return}if(r.shadowRoot){for(const e of r.shadowRoot.childNodes)n(e);return}}for(const e of r.childNodes)n(e)}};for(const e of this.childNodes)n(e);const r=this.overlayPortal();return r&&n(r),t.filter(e=>{let t=e;for(;t;){const e=getComputedStyle(t);if(t.hidden||t.inert||"true"===t.getAttribute("aria-hidden")||"none"===e.display||"hidden"===e.visibility)return!1;if(t=t.assignedSlot||t.parentElement||(t.getRootNode()instanceof ShadowRoot?t.getRootNode().host:null),t===this)break}return!0})}rejectClose(){this._closing=!1,this.requestUpdate()}_pruneOverlays(){this._overlays=this._overlays.filter(e=>e.owner.isConnected)}_closeOverlay(e,t){const i=this._overlays.findIndex(t=>t.token===e.token);i>=0&&this._overlays.splice(i,1),e.close(t)}registerOverlay(e){this._pruneOverlays();const t=this._overlays.find(t=>t.owner===e.owner);t&&this._overlays.splice(this._overlays.indexOf(t),1);const i=e.group||"transient";for(const e of[...this._overlays].reverse())e.group===i&&this._closeOverlay(e,"exclusive");const n={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(n);let r=!1;return()=>{if(r)return;r=!0;const e=this._overlays.findIndex(e=>e.token===n.token);e>=0&&this._overlays.splice(e,1)}}closeTransientOverlays(e="outside"){this._pruneOverlays();const t=[...this._overlays].filter(e=>"transient"===(e.group||"transient"));for(const i of t.reverse())this._closeOverlay(i,e);return t.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const e=W` ${this.icon?W``:V} ${this.title} `;return this._usesHaDialog()?this.describedBy?W`{this._active={...t,resolve:e},this._changed(t)})}resolve(e,t){const i=this._active;return!(!i||i.token!==e)&&(this._active=null,this._changed(null),i.resolve(t),!0)}cancel(){const e=this._active;return!!e&&this.resolve(e.token,!1)}}const fe=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),ve=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|thermometer|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function ye(e){const t=[];for(const i of e)if(i&&"string"==typeof i.pattern&&i.icon)try{t.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return t}function be(e){try{return new RegExp(e,"i"),!0}catch{return!1}}const we=ye(ve),ke={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",presence:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},xe="mdi:chip";function $e(e,t,i){const n=((e||"")+" "+(t||"")).toLowerCase();for(const{re:e,icon:t}of i??we)if(e.test(n))return t;return xe}const Se=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var Me=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,Ce=Math.ceil,Re=Math.floor,Te="[BigNumber Error] ",De=Te+"Number primitive has more than 15 significant digits: ",ze=1e14,Ae=14,Pe=9007199254740991,Fe=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],Oe=1e7,Ie=1e9;function Ee(e){var t=0|e;return e>0||e===t?t:t-1}function Ne(e){for(var t,i,n=1,r=e.length,o=e[0]+"";nc^i?1:-1;for(a=(l=r.length)<(c=o.length)?l:c,s=0;so[s]^i?1:-1;return l==c?0:l>c^i?1:-1}function Le(e,t,i,n){if(ei||e!==Re(e))throw Error(Te+(n||"Argument")+("number"==typeof e?ei?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function Be(e){var t=e.c.length-1;return Ee(e.e/Ae)==t&&e.c[t]%2!=0}function qe(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function We(e,t,i){var n,r;if(t<0){for(r=i+".";++t;r+=i);e=r+e}else if(++t>(n=e.length)){for(r=i,t-=n;--t;r+=i);e+=r}else ty?u.c=u.e=null:e.e=10;l/=10,a++);return void(a>y?u.c=u.e=null:(u.e=a,u.c=[e]))}d=String(e)}else{if(!Me.test(d=String(e)))return r(u,d,c);u.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(Le(t,2,$.length,"Base"),10==t&&S)return D(u=new M(e),m+u.e+1,_);if(d=String(e),c="number"==typeof e){if(0*e!=0)return r(u,d,c,t);if(u.s=1/e<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(De+e)}else u.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,t),a=l=0,h=d.length;la){a=h;continue}}else if(!s&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){s=!0,l=-1,a=0;continue}return r(u,String(e),c,t)}c=!1,(a=(d=n(d,t,10,u.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(e>Pe||e!==Re(e)))throw Error(De+u.s*e);if((a=a-l-1)>y)u.c=u.e=null;else if(a=f)?qe(l,s):We(l,s,"0");else if(o=(e=D(new M(e),t,i)).e,a=(l=Ne(e.c)).length,1==n||2==n&&(t<=o||o<=g)){for(;as),l=We(l,o,"0"),o+1>a){if(--t>0)for(l+=".";t--;l+="0");}else if((t+=o-a)>0)for(o+1==a&&(l+=".");t--;l+="0");return e.s<0&&r?"-"+l:l}function R(e,t){for(var i,n,r=1,o=new M(e[0]);r=10;r/=10,n++);return(i=n+i*Ae-1)>y?e.c=e.e=null:i=10;a/=10,r++);if((o=t-r)<0)o+=Ae,s=t,l=d[c=0],h=Re(l/u[r-s-1]%10);else if((c=Ce((o+1)/Ae))>=d.length){if(!n)break e;for(;d.length<=c;d.push(0));l=h=0,r=1,s=(o%=Ae)-Ae+1}else{for(l=a=d[c],r=1;a>=10;a/=10,r++);h=(s=(o%=Ae)-Ae+r)<0?0:Re(l/u[r-s-1]%10)}if(n=n||t<0||null!=d[c+1]||(s<0?l:l%u[r-s-1]),n=i<4?(h||n)&&(0==i||i==(e.s<0?3:2)):h>5||5==h&&(4==i||n||6==i&&(o>0?s>0?l/u[r-s]:0:d[c-1])%10&1||i==(e.s<0?8:7)),t<1||!d[0])return d.length=0,n?(t-=e.e+1,d[0]=u[(Ae-t%Ae)%Ae],e.e=-t||0):d[0]=e.e=0,e;if(0==o?(d.length=c,a=1,c--):(d.length=c+1,a=u[Ae-o],d[c]=s>0?Re(l/u[r-s]%u[s])*a:0),n)for(;;){if(0==c){for(o=1,s=d[0];s>=10;s/=10,o++);for(s=d[0]+=a,a=1;s>=10;s/=10,a++);o!=a&&(e.e++,d[0]==ze&&(d[0]=1));break}if(d[c]+=a,d[c]!=ze)break;d[c--]=0,a=1}for(o=d.length;0===d[--o];d.pop());}e.e>y?e.c=e.e=null:e.e=f?qe(t,i):We(t,i,"0"),e.s<0?"-"+t:t)}return M.clone=e,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(e){var t,i;if(null!=e){if("object"!=typeof e)throw Error(Te+"Object expected: "+e);if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(Le(i=e[t],0,Ie,t),m=i),e.hasOwnProperty(t="ROUNDING_MODE")&&(Le(i=e[t],0,8,t),_=i),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((i=e[t])&&i.pop?(Le(i[0],-Ie,0,t),Le(i[1],0,Ie,t),g=i[0],f=i[1]):(Le(i,-Ie,Ie,t),g=-(f=i<0?-i:i))),e.hasOwnProperty(t="RANGE"))if((i=e[t])&&i.pop)Le(i[0],-Ie,-1,t),Le(i[1],1,Ie,t),v=i[0],y=i[1];else{if(Le(i,-Ie,Ie,t),!i)throw Error(Te+t+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(e.hasOwnProperty(t="CRYPTO")){if((i=e[t])!==!!i)throw Error(Te+t+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(Te+"crypto unavailable");b=i}else b=i}if(e.hasOwnProperty(t="MODULO_MODE")&&(Le(i=e[t],0,9,t),w=i),e.hasOwnProperty(t="POW_PRECISION")&&(Le(i=e[t],0,Ie,t),k=i),e.hasOwnProperty(t="FORMAT")){if("object"!=typeof(i=e[t]))throw Error(Te+t+" not an object: "+i);x=i}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(i=e[t])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(Te+t+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:m,ROUNDING_MODE:_,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!M.DEBUG)return!0;var t,i,n=e.c,r=e.e,o=e.s;e:if("[object Array]"=={}.toString.call(n)){if((1===o||-1===o)&&r>=-Ie&&r<=Ie&&r===Re(r)){if(0===n[0]){if(0===r&&1===n.length)return!0;break e}if((t=(r+1)%Ae)<1&&(t+=Ae),String(n[0]).length==t){for(t=0;t=ze||i!==Re(i))break e;if(0!==i)return!0}}}else if(null===n&&null===r&&(null===o||1===o||-1===o))return!0;throw Error(Te+"Invalid BigNumber: "+e)},M.maximum=M.max=function(){return R(arguments,-1)},M.minimum=M.min=function(){return R(arguments,1)},M.random=(o=9007199254740992,s=Math.random()*o&2097151?function(){return Re(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(e){var t,i,n,r,o,a=0,l=[],c=new M(p);if(null==e?e=m:Le(e,0,Ie),r=Ce(e/Ae),b)if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(r*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),t[a]=i[0],t[a+1]=i[1]):(l.push(o%1e14),a+=2);a=r/2}else{if(!crypto.randomBytes)throw b=!1,Error(Te+"crypto unavailable");for(t=crypto.randomBytes(r*=7);a=9e15?crypto.randomBytes(7).copy(t,a):(l.push(o%1e14),a+=7);a=r/7}if(!b)for(;a=10;o/=10,a++);ai-1&&(null==s[r+1]&&(s[r+1]=0),s[r+1]+=s[r]/i|0,s[r]%=i)}return s.reverse()}return function(n,r,o,s,a){var l,c,h,d,u,p,g,f,v=n.indexOf("."),y=m,b=_;for(v>=0&&(d=k,k=0,n=n.replace(".",""),p=(f=new M(r)).pow(n.length-v),k=d,f.c=t(We(Ne(p.c),p.e,"0"),10,o,e),f.e=f.c.length),h=d=(g=t(n,r,o,a?(l=$,e):(l=e,$))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(p.c=g,p.e=h,p.s=s,g=(p=i(p,f,y,b,o)).c,u=p.r,h=p.e),v=g[c=h+y+1],d=o/2,u=u||c<0||null!=g[c+1],u=b<4?(null!=v||u)&&(0==b||b==(p.s<0?3:2)):v>d||v==d&&(4==b||u||6==b&&1&g[c-1]||b==(p.s<0?8:7)),c<1||!g[0])n=u?We(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,u)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,n="";v<=d;n+=l.charAt(g[v++]));n=We(n,h,l.charAt(0))}return n}}(),i=function(){function e(e,t,i){var n,r,o,s,a=0,l=e.length,c=t%Oe,h=t/Oe|0;for(e=e.slice();l--;)a=((r=c*(o=e[l]%Oe)+(n=h*o+(s=e[l]/Oe|0)*c)%Oe*Oe+a)/i|0)+(n/Oe|0)+h*s,e[l]=r%i;return a&&(e=[a].concat(e)),e}function t(e,t,i,n){var r,o;if(i!=n)o=i>n?1:-1;else for(r=o=0;rt[r]?1:-1;break}return o}function i(e,t,i,n){for(var r=0;i--;)e[i]-=r,r=e[i]1;e.splice(0,1));}return function(n,r,o,s,a){var l,c,h,d,u,p,m,_,g,f,v,y,b,w,k,x,$,S=n.s==r.s?1:-1,C=n.c,R=r.c;if(!(C&&C[0]&&R&&R[0]))return new M(n.s&&r.s&&(C?!R||C[0]!=R[0]:R)?C&&0==C[0]||!R?0*S:S/0:NaN);for(g=(_=new M(S)).c=[],S=o+(c=n.e-r.e)+1,a||(a=ze,c=Ee(n.e/Ae)-Ee(r.e/Ae),S=S/Ae|0),h=0;R[h]==(C[h]||0);h++);if(R[h]>(C[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=C.length,x=R.length,h=0,S+=2,(u=Re(a/(R[0]+1)))>1&&(R=e(R,u,a),C=e(C,u,a),x=R.length,w=C.length),b=x,v=(f=C.slice(0,x)).length;v=a/2&&k++;do{if(u=0,(l=t(R,f,x,v))<0){if(y=f[0],x!=v&&(y=y*a+(f[1]||0)),(u=Re(y/k))>1)for(u>=a&&(u=a-1),m=(p=e(R,u,a)).length,v=f.length;1==t(p,f,m,v);)u--,i(p,x=10;S/=10,h++);D(_,o+(_.e=h+c*Ae-1)+1,s,d)}else _.e=c,_.r=+d;return _}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,r=function(e,t,i,n){var r,o=i?t:t.replace(d,"");if(h.test(o))e.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(a,function(e,t,i){return r="x"==(i=i.toLowerCase())?16:"b"==i?2:8,n&&n!=r?e:t}),n&&(r=n,o=o.replace(l,"$1").replace(c,"0.$1")),t!=o))return new M(o,r);if(M.DEBUG)throw Error(Te+"Not a"+(n?" base "+n:"")+" number: "+t);e.s=null}e.c=e.e=null},u.absoluteValue=u.abs=function(){var e=new M(this);return e.s<0&&(e.s=1),e},u.comparedTo=function(e,t){return He(this,new M(e,t))},u.decimalPlaces=u.dp=function(e,t){var i,n,r,o=this;if(null!=e)return Le(e,0,Ie),null==t?t=_:Le(t,0,8),D(new M(o),e+o.e+1,t);if(!(i=o.c))return null;if(n=((r=i.length-1)-Ee(this.e/Ae))*Ae,r=i[r])for(;r%10==0;r/=10,n--);return n<0&&(n=0),n},u.dividedBy=u.div=function(e,t){return i(this,new M(e,t),m,_)},u.dividedToIntegerBy=u.idiv=function(e,t){return i(this,new M(e,t),0,1)},u.exponentiatedBy=u.pow=function(e,t){var i,n,r,o,s,a,l,c,h=this;if((e=new M(e)).c&&!e.isInteger())throw Error(Te+"Exponent not an integer: "+z(e));if(null!=t&&(t=new M(t)),s=e.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!e.c||!e.c[0])return c=new M(Math.pow(+z(h),s?e.s*(2-Be(e)):+z(e))),t?c.mod(t):c;if(a=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new M(NaN);(n=!a&&h.isInteger()&&t.isInteger())&&(h=h.mod(t))}else{if(e.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||s&&h.c[1]>=24e7:h.c[0]<8e13||s&&h.c[0]<=9999975e7)))return o=h.s<0&&Be(e)?-0:0,h.e>-1&&(o=1/o),new M(a?1/o:o);k&&(o=Ce(k/Ae+2))}for(s?(i=new M(.5),a&&(e.s=1),l=Be(e)):l=(r=Math.abs(+z(e)))%2,c=new M(p);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):n&&(c=c.mod(t))}if(r){if(0===(r=Re(r/2)))break;l=r%2}else if(D(e=e.times(i),e.e+1,1),e.e>14)l=Be(e);else{if(0===(r=+z(e)))break;l=r%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):n&&(h=h.mod(t))}return n?c:(a&&(c=p.div(c)),t?c.mod(t):o?D(c,k,_,void 0):c)},u.integerValue=function(e){var t=new M(this);return null==e?e=_:Le(e,0,8),D(t,t.e+1,e)},u.isEqualTo=u.eq=function(e,t){return 0===He(this,new M(e,t))},u.isFinite=function(){return!!this.c},u.isGreaterThan=u.gt=function(e,t){return He(this,new M(e,t))>0},u.isGreaterThanOrEqualTo=u.gte=function(e,t){return 1===(t=He(this,new M(e,t)))||0===t},u.isInteger=function(){return!!this.c&&Ee(this.e/Ae)>this.c.length-2},u.isLessThan=u.lt=function(e,t){return He(this,new M(e,t))<0},u.isLessThanOrEqualTo=u.lte=function(e,t){return-1===(t=He(this,new M(e,t)))||0===t},u.isNaN=function(){return!this.s},u.isNegative=function(){return this.s<0},u.isPositive=function(){return this.s>0},u.isZero=function(){return!!this.c&&0==this.c[0]},u.minus=function(e,t){var i,n,r,o,s=this,a=s.s;if(t=(e=new M(e,t)).s,!a||!t)return new M(NaN);if(a!=t)return e.s=-t,s.plus(e);var l=s.e/Ae,c=e.e/Ae,h=s.c,d=e.c;if(!l||!c){if(!h||!d)return h?(e.s=-t,e):new M(d?s:NaN);if(!h[0]||!d[0])return d[0]?(e.s=-t,e):new M(h[0]?s:3==_?-0:0)}if(l=Ee(l),c=Ee(c),h=h.slice(),a=l-c){for((o=a<0)?(a=-a,r=h):(c=l,r=d),r.reverse(),t=a;t--;r.push(0));r.reverse()}else for(n=(o=(a=h.length)<(t=d.length))?a:t,a=t=0;t0)for(;t--;h[i++]=0);for(t=ze-1;n>a;){if(h[--n]=0;){for(i=0,u=y[r]%g,p=y[r]/g|0,o=r+(s=l);o>r;)i=((c=u*(c=v[--s]%g)+(a=p*c+(h=v[s]/g|0)*u)%g*g+m[o]+i)/_|0)+(a/g|0)+p*h,m[o--]=c%_;m[o]=i}return i?++n:m.splice(0,1),T(e,m,n)},u.negated=function(){var e=new M(this);return e.s=-e.s||null,e},u.plus=function(e,t){var i,n=this,r=n.s;if(t=(e=new M(e,t)).s,!r||!t)return new M(NaN);if(r!=t)return e.s=-t,n.minus(e);var o=n.e/Ae,s=e.e/Ae,a=n.c,l=e.c;if(!o||!s){if(!a||!l)return new M(r/0);if(!a[0]||!l[0])return l[0]?e:new M(a[0]?n:0*r)}if(o=Ee(o),s=Ee(s),a=a.slice(),r=o-s){for(r>0?(s=o,i=l):(r=-r,i=a),i.reverse();r--;i.push(0));i.reverse()}for((r=a.length)-(t=l.length)<0&&(i=l,l=a,a=i,t=r),r=0;t;)r=(a[--t]=a[t]+l[t]+r)/ze|0,a[t]=ze===a[t]?0:a[t]%ze;return r&&(a=[r].concat(a),++s),T(e,a,s)},u.precision=u.sd=function(e,t){var i,n,r,o=this;if(null!=e&&e!==!!e)return Le(e,1,Ie),null==t?t=_:Le(t,0,8),D(new M(o),e,t);if(!(i=o.c))return null;if(n=(r=i.length-1)*Ae+1,r=i[r]){for(;r%10==0;r/=10,n--);for(r=i[0];r>=10;r/=10,n++);}return e&&o.e+1>n&&(n=o.e+1),n},u.shiftedBy=function(e){return Le(e,-9007199254740991,Pe),this.times("1e"+e)},u.squareRoot=u.sqrt=function(){var e,t,n,r,o,s=this,a=s.c,l=s.s,c=s.e,h=m+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?s:1/0);if(0==(l=Math.sqrt(+z(s)))||l==1/0?(((t=Ne(a)).length+c)%2==0&&(t+="0"),l=Math.sqrt(+t),c=Ee((c+1)/2)-(c<0||c%2),n=new M(t=l==1/0?"5e"+c:(t=l.toExponential()).slice(0,t.indexOf("e")+1)+c)):n=new M(l+""),n.c[0])for((l=(c=n.e)+h)<3&&(l=0);;)if(o=n,n=d.times(o.plus(i(s,o,h,1))),Ne(o.c).slice(0,l)===(t=Ne(n.c)).slice(0,l)){if(n.e0&&m>0){for(o=m%a||a,h=p.substr(0,o);o0&&(h+=c+p.slice(o)),u&&(h="-"+h)}n=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+n+(i.suffix||"")},u.toFraction=function(e){var t,n,r,o,s,a,l,c,h,d,u,m,g=this,f=g.c;if(null!=e&&(!(l=new M(e)).isInteger()&&(l.c||1!==l.s)||l.lt(p)))throw Error(Te+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+z(l));if(!f)return new M(g);for(t=new M(p),h=n=new M(p),r=c=new M(p),m=Ne(f),s=t.e=m.length-g.e-1,t.c[0]=Fe[(a=s%Ae)<0?Ae+a:a],e=!e||l.comparedTo(t)>0?s>0?t:h:l,a=y,y=1/0,l=new M(m),c.c[0]=0;d=i(l,t,0,1),1!=(o=n.plus(d.times(r))).comparedTo(e);)n=r,r=o,h=c.plus(d.times(o=h)),c=o,t=l.minus(d.times(o=t)),l=o;return o=i(e.minus(n),r,0,1),c=c.plus(o.times(h)),n=n.plus(o.times(r)),c.s=h.s=g.s,u=i(h,r,s*=2,_).minus(g).abs().comparedTo(i(c,n,s,_).minus(g).abs())<1?[h,r]:[c,n],y=a,u},u.toNumber=function(){return+z(this)},u.toPrecision=function(e,t){return null!=e&&Le(e,1,Ie),C(this,e,t,2)},u.toString=function(e){var t,i=this,r=i.s,o=i.e;return null===o?r?(t="Infinity",r<0&&(t="-"+t)):t="NaN":(null==e?t=o<=g||o>=f?qe(Ne(i.c),o):We(Ne(i.c),o,"0"):10===e&&S?t=We(Ne((i=D(new M(i),m+o+1,_)).c),i.e,"0"):(Le(e,2,$.length,"Base"),t=n(We(Ne(i.c),o,"0"),10,e,r,!0)),r<0&&i.c[0]&&(t="-"+t)),t},u.valueOf=u.toJSON=function(){return z(this)},u._isBigNumber=!0,u[Symbol.toStringTag]="BigNumber",u[Symbol.for("nodejs.util.inspect.custom")]=u.valueOf,null!=t&&M.set(t),M}(),Ue=class{key;left=null;right=null;constructor(e){this.key=e}},Ve=class extends Ue{constructor(e){super(e)}},Ge=class{size=0;modificationCount=0;splayCount=0;splay(e){const t=this.root;if(null==t)return this.compare(e,e),-1;let i=null,n=null,r=null,o=null,s=t;const a=this.compare;let l;for(;;)if(l=a(s.key,e),l>0){let t=s.left;if(null==t)break;if(l=a(t.key,e),l>0&&(s.left=t.right,t.right=s,s=t,t=s.left,null==t))break;null==i?n=s:i.left=s,i=s,s=t}else{if(!(l<0))break;{let t=s.right;if(null==t)break;if(l=a(t.key,e),l<0&&(s.right=t.left,t.left=s,s=t,t=s.right,null==t))break;null==r?o=s:r.right=s,r=s,s=t}}return null!=r&&(r.right=s.left,s.left=o),null!=i&&(i.left=s.right,s.right=n),this.root!==s&&(this.root=s,this.splayCount++),l}splayMin(e){let t=e,i=t.left;for(;null!=i;){const e=i;t.left=e.right,e.right=t,t=e,i=t.left}return t}splayMax(e){let t=e,i=t.right;for(;null!=i;){const e=i;t.right=e.left,e.left=t,t=e,i=t.right}return t}_delete(e){if(null==this.root)return null;if(0!=this.splay(e))return null;let t=this.root;const i=t,n=t.left;if(this.size--,null==n)this.root=t.right;else{const e=t.right;t=this.splayMax(n),t.right=e,this.root=t}return this.modificationCount++,i}addNewRoot(e,t){this.size++,this.modificationCount++;const i=this.root;null!=i?(t<0?(e.left=i,e.right=i.right,i.right=null):(e.right=i,e.left=i.left,i.left=null),this.root=e):this.root=e}_first(){const e=this.root;return null==e?null:(this.root=this.splayMin(e),this.root)}_last(){const e=this.root;return null==e?null:(this.root=this.splayMax(e),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(e){return this.validKey(e)&&0==this.splay(e)}defaultCompare(){return(e,t)=>et?1:0}wrap(){return{getRoot:()=>this.root,setRoot:e=>{this.root=e},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:e=>{this.splayCount=e},splay:e=>this.splay(e),has:e=>this.has(e)}}},Ke=class e extends Ge{root=null;compare;validKey;constructor(e,t){super(),this.compare=e??this.defaultCompare(),this.validKey=t??(e=>null!=e&&null!=e)}delete(e){return!!this.validKey(e)&&null!=this._delete(e)}deleteAll(e){for(const t of e)this.delete(t)}forEach(e){const t=this[Symbol.iterator]();let i;for(;i=t.next(),!i.done;)e(i.value,i.value,this)}add(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new Ve(e),t),this}addAndReturn(e){const t=this.splay(e);return 0!=t&&this.addNewRoot(new Ve(e),t),this.root.key}addAll(e){for(const t of e)this.add(t)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)<0)return this.root.key;let t=this.root.left;if(null==t)return null;let i=t.right;for(;null!=i;)t=i,i=t.right;return t.key}firstAfter(e){if(null==e)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(e)>0)return this.root.key;let t=this.root.right;if(null==t)return null;let i=t.left;for(;null!=i;)t=i,i=t.left;return t.key}retainAll(t){const i=new e(this.compare,this.validKey),n=this.modificationCount;for(const e of t){if(n!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(e)&&0==this.splay(e)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(e){if(!this.validKey(e))return null;return 0!=this.splay(e)?null:this.root.key}intersection(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)&&i.add(e);return i}difference(t){const i=new e(this.compare,this.validKey);for(const e of this)t.has(e)||i.add(e);return i}union(e){const t=this.clone();return t.addAll(e),t}clone(){const t=new e(this.compare,this.validKey);return t.size=this.size,t.root=this.copyNode(this.root),t}copyNode(e){if(null==e)return null;const t=new Ve(e.key);return function e(t,i){let n,r;do{if(n=t.left,r=t.right,null!=n){const t=new Ve(n.key);i.left=t,e(n,t)}if(null!=r){const e=new Ve(r.key);i.right=e,t=r,i=e}}while(null!=r)}(e,t),t}toSet(){return this.clone()}entries(){return new Ze(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Xe(this.wrap())}[Symbol.toStringTag]="[object Set]"},Ye=class{tree;path=new Array;modificationCount=null;splayCount;constructor(e){this.tree=e,this.splayCount=e.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const e=this.path[this.path.length-1];return this.getValue(e)}rebuildPath(e){this.path.splice(0,this.path.length),this.tree.splay(e),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(e){for(;null!=e;)this.path.push(e),e=e.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let e=this.tree.getRoot();for(;null!=e;)this.path.push(e),e=e.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let e=this.path[this.path.length-1],t=e.right;if(null!=t){for(;null!=t;)this.path.push(t),t=t.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===e;)e=this.path.pop();return this.path.length>0}},Xe=class extends Ye{getValue(e){return e.key}},Ze=class extends Ye{getValue(e){return[e.key,e.key]}},Je=e=>()=>e,Qe=e=>{const t=e?(t,i)=>i.minus(t).abs().isLessThanOrEqualTo(e):Je(!1);return(e,i)=>t(e,i)?0:e.comparedTo(i)};function et(e){const t=e?(t,i,n,r,o)=>t.exponentiatedBy(2).isLessThanOrEqualTo(r.minus(i).exponentiatedBy(2).plus(o.minus(n).exponentiatedBy(2)).times(e)):Je(!1);return(e,i,n)=>{const r=e.x,o=e.y,s=n.x,a=n.y,l=o.minus(a).times(i.x.minus(s)).minus(r.minus(s).times(i.y.minus(a)));return t(l,r,o,s,a)?0:l.comparedTo(0)}}var tt=e=>e,it=e=>{if(e){const t=new Ke(Qe(e)),i=new Ke(Qe(e)),n=(e,t)=>t.addAndReturn(e),r=e=>({x:n(e.x,t),y:n(e.y,i)});return r({x:new je(0),y:new je(0)}),r}return tt},nt=e=>({set:e=>{rt=nt(e)},reset:()=>nt(e),compare:Qe(e),snap:it(e),orient:et(e)}),rt=nt(),ot=(e,t)=>e.ll.x.isLessThanOrEqualTo(t.x)&&t.x.isLessThanOrEqualTo(e.ur.x)&&e.ll.y.isLessThanOrEqualTo(t.y)&&t.y.isLessThanOrEqualTo(e.ur.y),st=(e,t)=>{if(t.ur.x.isLessThan(e.ll.x)||e.ur.x.isLessThan(t.ll.x)||t.ur.y.isLessThan(e.ll.y)||e.ur.y.isLessThan(t.ll.y))return null;const i=e.ll.x.isLessThan(t.ll.x)?t.ll.x:e.ll.x,n=e.ur.x.isLessThan(t.ur.x)?e.ur.x:t.ur.x;return{ll:{x:i,y:e.ll.y.isLessThan(t.ll.y)?t.ll.y:e.ll.y},ur:{x:n,y:e.ur.y.isLessThan(t.ur.y)?e.ur.y:t.ur.y}}},at=(e,t)=>e.x.times(t.y).minus(e.y.times(t.x)),lt=(e,t)=>e.x.times(t.x).plus(e.y.times(t.y)),ct=e=>lt(e,e).sqrt(),ht=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return at(r,n).div(ct(r)).div(ct(n))},dt=(e,t,i)=>{const n={x:t.x.minus(e.x),y:t.y.minus(e.y)},r={x:i.x.minus(e.x),y:i.y.minus(e.y)};return lt(r,n).div(ct(r)).div(ct(n))},ut=(e,t,i)=>t.y.isZero()?null:{x:e.x.plus(t.x.div(t.y).times(i.minus(e.y))),y:i},pt=(e,t,i)=>t.x.isZero()?null:{x:i,y:e.y.plus(t.y.div(t.x).times(i.minus(e.x)))},mt=class e{point;isLeft;segment;otherSE;consumedBy;static compare(t,i){const n=e.comparePoints(t.point,i.point);return 0!==n?n:(t.point!==i.point&&t.link(i),t.isLeft!==i.isLeft?t.isLeft?1:-1:kt.compare(t.segment,i.segment))}static comparePoints(e,t){return e.x.isLessThan(t.x)?-1:e.x.isGreaterThan(t.x)?1:e.y.isLessThan(t.y)?-1:e.y.isGreaterThan(t.y)?1:0}constructor(e,t){void 0===e.events?e.events=[this]:e.events.push(this),this.point=e,this.isLeft=t}link(e){if(e.point===this.point)throw new Error("Tried to link already linked events");const t=e.point.events;for(let e=0,i=t.length;e{const n=i.otherSE;t.set(i,{sine:ht(this.point,e.point,n.point),cosine:dt(this.point,e.point,n.point)})};return(e,n)=>{t.has(e)||i(e),t.has(n)||i(n);const{sine:r,cosine:o}=t.get(e),{sine:s,cosine:a}=t.get(n);return r.isGreaterThanOrEqualTo(0)&&s.isGreaterThanOrEqualTo(0)?o.isLessThan(a)?1:o.isGreaterThan(a)?-1:0:r.isLessThan(0)&&s.isLessThan(0)?o.isLessThan(a)?-1:o.isGreaterThan(a)?1:0:s.isLessThan(r)?-1:s.isGreaterThan(r)?1:0}}},_t=class e{events;poly;_isExteriorRing;_enclosingRing;static factory(t){const i=[];for(let n=0,r=t.length;n0&&(e=i)}let t=e.segment.prevInResult(),i=t?t.prevInResult():null;for(;;){if(!t)return null;if(!i)return t.ringOut;if(i.ringOut!==t.ringOut)return i.ringOut?.enclosingRing()!==t.ringOut?t.ringOut:t.ringOut?.enclosingRing();t=i.prevInResult(),i=t?t.prevInResult():null}}},gt=class{exteriorRing;interiorRings;constructor(e){this.exteriorRing=e,e.poly=this,this.interiorRings=[]}addInterior(e){this.interiorRings.push(e),e.poly=this}getGeom(){const e=this.exteriorRing.getGeom();if(null===e)return null;const t=[e];for(let e=0,i=this.interiorRings.length;e0?(this.tree.delete(t),i.push(e)):(this.segments.push(t),t.prev=n)}else{if(n&&r){const e=n.getIntersection(r);if(null!==e){if(!n.isAnEndpoint(e)){const t=this._splitSafely(n,e);for(let e=0,n=t.length;e0)return-1;const n=t.comparePoint(e.rightSE.point);return 0!==n?n:-1}if(i.isGreaterThan(n)){if(s.isLessThan(a)&&s.isLessThan(c))return-1;if(s.isGreaterThan(a)&&s.isGreaterThan(c))return 1;const i=t.comparePoint(e.leftSE.point);if(0!==i)return i;const n=e.comparePoint(t.rightSE.point);return n<0?1:n>0?-1:1}if(s.isLessThan(a))return-1;if(s.isGreaterThan(a))return 1;if(r.isLessThan(o)){const i=t.comparePoint(e.rightSE.point);if(0!==i)return i}if(r.isGreaterThan(o)){const i=e.comparePoint(t.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!r.eq(o)){const e=l.minus(s),t=r.minus(i),h=c.minus(a),d=o.minus(n);if(e.isGreaterThan(t)&&h.isLessThan(d))return 1;if(e.isLessThan(t)&&h.isGreaterThan(d))return-1}return r.isGreaterThan(o)?1:r.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:e.idt.id?1:0}constructor(e,t,i,n){this.id=++wt,this.leftSE=e,e.segment=this,e.otherSE=t,this.rightSE=t,t.segment=this,t.otherSE=e,this.rings=i,this.windings=n}static fromRing(t,i,n){let r,o,s;const a=mt.comparePoints(t,i);if(a<0)r=t,o=i,s=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${t.x}, ${t.y}]`);r=i,o=t,s=-1}const l=new mt(r,!0),c=new mt(o,!1);return new e(l,c,[n],[s])}replaceRightSE(e){this.rightSE=e,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const e=this.leftSE.point.y,t=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:e.isLessThan(t)?e:t},ur:{x:this.rightSE.point.x,y:e.isGreaterThan(t)?e:t}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(e){return e.x.eq(this.leftSE.point.x)&&e.y.eq(this.leftSE.point.y)||e.x.eq(this.rightSE.point.x)&&e.y.eq(this.rightSE.point.y)}comparePoint(e){return rt.orient(this.leftSE.point,e,this.rightSE.point)}getIntersection(e){const t=this.bbox(),i=e.bbox(),n=st(t,i);if(null===n)return null;const r=this.leftSE.point,o=this.rightSE.point,s=e.leftSE.point,a=e.rightSE.point,l=ot(t,s)&&0===this.comparePoint(s),c=ot(i,r)&&0===e.comparePoint(r),h=ot(t,a)&&0===this.comparePoint(a),d=ot(i,o)&&0===e.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?a:null;if(c)return h&&r.x.eq(a.x)&&r.y.eq(a.y)?null:r;if(l)return d&&o.x.eq(s.x)&&o.y.eq(s.y)?null:s;if(d&&h)return null;if(d)return o;if(h)return a;const u=((e,t,i,n)=>{if(t.x.isZero())return pt(i,n,e.x);if(n.x.isZero())return pt(e,t,i.x);if(t.y.isZero())return ut(i,n,e.y);if(n.y.isZero())return ut(e,t,i.y);const r=at(t,n);if(r.isZero())return null;const o={x:i.x.minus(e.x),y:i.y.minus(e.y)},s=at(o,t).div(r),a=at(o,n).div(r),l=e.x.plus(a.times(t.x)),c=i.x.plus(s.times(n.x)),h=e.y.plus(a.times(t.y)),d=i.y.plus(s.times(n.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(r,this.vector(),s,e.vector());return null===u?null:ot(n,u)?rt.snap(u):null}split(t){const i=[],n=void 0!==t.events,r=new mt(t,!0),o=new mt(t,!1),s=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(r);const a=new e(r,s,this.rings.slice(),this.windings.slice());return mt.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),mt.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),n&&(r.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const e=this.rightSE;this.rightSE=this.leftSE,this.leftSE=e,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let e=0,t=this.windings.length;e0){const e=i;i=n,n=e}if(i.prev===n){const e=i;i=n,n=e}for(let e=0,t=n.rings.length;e1===e.length&&e[0].isSubject;this._isInResult=i(e)!==i(t);break}}return this._isInResult}},xt=class{poly;isExterior;segments;bbox;constructor(e,t,i){if(!Array.isArray(e)||0===e.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=t,this.isExterior=i,this.segments=[],"number"!=typeof e[0][0]||"number"!=typeof e[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const n=rt.snap({x:new je(e[0][0]),y:new je(e[0][1])});this.bbox={ll:{x:n.x,y:n.y},ur:{x:n.x,y:n.y}};let r=n;for(let t=1,i=e.length;tbt.run("union",e,t),Ct=(e,...t)=>bt.run("intersection",e,t),Rt=(e,...t)=>bt.run("difference",e,t);rt.set;const Tt=/^#[0-9a-fA-F]{6}$/;function Dt(e,t){return"string"==typeof e&&Tt.test(e)?e:t}const zt="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",At=new RegExp(`^rgb\\(${zt}, ${zt}, ${zt}\\)$`);function Pt(e){if(!Array.isArray(e)||e.length<3)return null;const t=e.slice(0,3);if(!t.every(e=>"number"==typeof e&&Number.isFinite(e)))return null;const[i,n,r]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${n}, ${r})`}function Ft(e){const t=Math.max(0,Math.min(120,(e-40)/140*120));return`hsl(${Math.round(t)}, 85%, 55%)`}function Ot(e,t){if(!(Number.isFinite(e)&&t>0))return e;const i=Math.round(e/t)*t;return Math.abs(i-e)<=1e-9*t?e:i}function It(e,t,i,n=1/0){if(!(i>0&&e?.every(Number.isFinite)&&t?.every(Number.isFinite)))return[t[0],t[1]];const r=t[0]-e[0],o=t[1]-e[1];if(Math.abs(r)+Math.abs(o)<=1e-12)return[e[0],e[1]];const s=Math.PI/4;let a=Math.atan2(o,r);a<0&&(a+=2*Math.PI);const l=Math.floor(a/s+.5)%8,[c,h]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=c*c+h*h;let u=Math.max(0,Math.round((r*c+o*h)/(i*d)));if(Number.isFinite(n)&&n>=0){const t=(e,t)=>{if(!t)return 1/0;const r=t>0?n-e:e+n;return Math.max(0,Math.floor((r+1e-9*i)/i))};u=Math.min(u,t(e[0],c),t(e[1],h))}return[e[0]+c*u*i,e[1]+h*u*i]}function Et(e,t){if(t){const t=e/2.54;let i=Math.floor(t/12),n=Math.round(t-12*i);return 12===n&&(i+=1,n=0),`${i}′ ${n}″`}return`${(e/100).toFixed(2)} m`}function Nt(e,t,i=1){const n=e[0].toFixed(i),r=e[1].toFixed(i),o=t[0].toFixed(i),s=t[1].toFixed(i),a=n=3?e.poly:e&&null!=e.x&&null!=e.y&&null!=e.w&&null!=e.h?[[e.x,e.y],[e.x+e.w,e.y],[e.x+e.w,e.y+e.h],[e.x,e.y+e.h]]:null}function Lt(e){const t=[];for(const i of e||[])i?.poly?.length>=3?t.push({poly:i.poly.map(e=>e.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&t.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return t}function Bt(e){const t=[],i=new Set;for(const n of e||[]){const e=Ht(n);if(e)for(let n=0;n=90?e-=180:e<-90&&(e+=180),n.step&&n.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(n.length||0,0)/2,i/2);let a=Math.round(u*i/n.step)*n.step;Math.abs(u*i-i/2)<=n.step/2&&(a=i/2),a=Math.max(o,Math.min(i-o,a));const l=a/i;r={x:t+l*c,y:s+l*h,angle:e}}else r={x:p[0],y:p[1],angle:e}}}return r}function Wt(e,t,i){let n=null,r=1/0;for(let o=0;o=r)continue;r=m;const _=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(_,Math.round(p*_/i)*i)):p*_)/_;n=[s+g*h,a+g*d]}return n}function jt(e,t,i,n,r,o=1){const s=t*Math.PI/180,a=[Math.cos(s),Math.sin(s)];let l=null,c=o;for(const t of Bt(n)){const i=[[t[0],t[1]],[t[2],t[3]]],n=t=>Math.abs(a[0]*(t[1]-e[1])-a[1]*(t[0]-e[0]));if(n(i[0])>o||n(i[1])>o)continue;const r=(i[0][0]-e[0])*a[0]+(i[0][1]-e[1])*a[1],s=(i[1][0]-e[0])*a[0]+(i[1][1]-e[1])*a[1],h=Math.min(r,s),d=Math.max(r,s),u=h>0?h:d<0?-d:0;u[e[0]+a[0]*t,e[1]+a[1]*t],g=(h+d)/2;return{wallA:_(h),wallB:_(d),sideA:p,sideB:m,midA:_((h-u)/2),midB:_((u+d)/2),wallCenter:_(g),centered:Math.abs(g)<=r}}function Ut(e,t,i=!1,n){if("passage"===e)return 1;if(null==t||"unavailable"===t||"unknown"===t)return"window"===e?0:1;const r="string"==typeof n?n.trim()?Number(n):NaN:Number(n),o=null!=n&&Number.isFinite(r)?Math.max(0,Math.min(1,r/100)):function(e){return["on","open","home","detected","playing","cleaning"].includes(String(e))}(t)?1:0;return i?1-o:o}function Vt(e){const t=Number.isFinite(e)?Math.max(0,Math.min(1,e)):0;return Math.min(1,Math.max(0,.05*Math.round(t/.05)))}function Gt(e,t){return(Number.isFinite(e)&&e>0?e:0)*(Number.isFinite(t)?Math.max(0,Math.min(1,t)):0)}function Kt(e){return"passage"===e.type?[]:[e.contact,e.lock].filter(e=>"string"==typeof e&&e.length>0)}function Yt(e,t,i=200){const n=t.trim().toLowerCase();return(n?e.filter(e=>e.label.toLowerCase().includes(n)||e.value.toLowerCase().includes(n)):e).slice(0,Math.max(0,i))}function Xt(e,t,i=.001){return Math.abs(e[0]-t[0])e[1]!=l>e[1]&&e[0]<(a-o)*(e[1]-s)/(l-s)+o&&(i=!i)}return i}function Jt(e,t,i){const n=i[0]-t[0],r=i[1]-t[1],o=n*n+r*r;let s=o?((e[0]-t[0])*n+(e[1]-t[1])*r)/o:0;return s=Math.max(0,Math.min(1,s)),Math.hypot(e[0]-(t[0]+s*n),e[1]-(t[1]+s*r))}function Qt(e,t){if(!t||t.length<2)return null;let i=null,n=1/0;for(let r=0;rr&&s<-r||o<-r&&s>r)&&(a>r&&l<-r||a<-r&&l>r)}function ri(e,t=24){const i=e.map(e=>e[0]),n=e.map(e=>e[1]),r=Math.min(...i),o=Math.max(...i),s=Math.min(...n),a=Math.max(...n),l=Math.max(o-r,a-s)||1;let c=0,h=0,d=0;for(let t=0;t1e-9?[h/(3*c),d/(3*c)]:[(r+o)/2,(s+a)/2],p=(t,i)=>{const n=((t,i)=>{if(!Zt([t,i],e))return-1/0;let n=1/0;for(let r=0;r_&&(_=c,m=[n,l])}if(m){const[e,i]=m,n=(o-r)/t,l=(a-s)/t;for(let t=-4;t<=4;t++)for(let r=-4;r<=4;r++){const o=e+n*t/4,s=i+l*r/4,a=p(o,s);a>_&&(_=a,m=[o,s])}}return m||oi(e)||e[0]}function oi(e,t=1e-6){if(!e||e.length<3)return null;const i=e.length,n=[e.reduce((e,t)=>e+t[0],0)/i,e.reduce((e,t)=>e+t[1],0)/i];if(ti(n,e,t))return n;for(let n=0;nai(e,t,i));return n.filter(e=>!n.some(t=>t!==e&&ai(t,e,i)))}function hi(e){if(!e||e.length<3)return 0;let t=0;for(let i=0;i[e[0],e[1]]),[e[0][0],e[0][1]]]]}function ui(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=Mt(di(e),di(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const n=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return n.length>=3?n:null}function pi(e,t,i){for(let n=0;n1&&Xt(i[0],i[i.length-1],t)&&i.pop(),i}function _i(e,t,i=1e-6){if(!e||e.length<3||!t||t.length<2)return null;const n=t[0],r=t[t.length-1];if(Xt(n,r,i))return null;const o=pi(e,n,i),s=pi(e,r,i);if(o<0||s<0)return null;const a=t.slice(1,-1);for(const t of a)if(!ti(t,e,i))return null;for(let i=0;i{const s=[t];let a=(n+1)%e.length;for(let t=0;t<=e.length&&(s.push(e[a]),a!==o);t++)a=(a+1)%e.length;return s.push(r),mi(s,i)};let c,h;if(o===s){const s=mi([...t],i);if(s.length<3||hi(s)<=i)return null;const a=[];for(let i=0;i=0?t:[...t].reverse();for(const e of i)a.push(e)}c=mi(a,i),h=s}else c=mi([...l(n,o,r,s),...[...a].reverse()],i),h=mi([...l(r,s,n,o),...a],i);return c.length<3||h.length<3||hi(c)<=i||hi(h)<=i||Math.abs(hi(c)+hi(h)-hi(e))>Math.max(i,1e-6*hi(e))?null:[c,h]}function gi(e,t,i){const[n,r]=e.split(":");return"device"===n?r:"entity"===n?"lg_"+r:t&&t.startsWith("v_")?t:i()}function fi(e){return e.length?Math.round(e.reduce((e,t)=>e+t,0)/e.length):null}function vi(e,t){if(t>e[2]/e[3]){const i=e[3],n=e[3]*t;return{x:e[0]-(n-e[2])/2,y:e[1],w:n,h:i}}const i=e[2],n=e[2]/t;return{x:e[0],y:e[1]-(n-e[3])/2,w:i,h:n}}function yi(e,t,i,n){if(e.length<2)return;const r=t.x+n,o=t.x+t.w-n,s=t.y+n,a=t.y+t.h-n;for(let t=0;t<60;t++){let t=!1;for(let n=0;n=0)i=t.slice(0,r).trim(),n=t.slice(r+1).trim();else{const e=t.split(".");e.length>2&&(i=e.slice(0,2).join("."),n=e.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?r>=0&&!n||n&&!/^[a-zA-Z0-9_.-]+$/.test(n)?null:n?{entity:i,attr:n}:{entity:i}:null}function zi(e,t){const i=String(e??"").trim(),n=String(t??"").trim(),r=Di(n?`${i}:${n}`:i);return r?`{${r.entity}${r.attr?`:${r.attr}`:""}}`:""}function Ai(e,t,i){const n=String(t??"").trim();if(!n)return null;const r=e?.states?.[n];if(!r)return null;const o=String(i??"").trim(),s=e=>e.slice(0,60);if(o){const t=function(e){if(null==e)return null;if(Array.isArray(e)){const t=e.map(e=>null==e?"":String(e)).join(", ");return t?t.slice(0,60):null}if("object"==typeof e)return null;const t=String(e);return""===t?null:t.slice(0,60)}(r.attributes?.[o]);if(null===t)return null;const i=e?.formatEntityAttributeValue;if("function"==typeof i)try{const t=i.call(e,r,o);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:t,formatted:!1}}const a=r.state;if(null==a||""===a)return null;const l=e?.formatEntityState;if("function"==typeof l)try{const t=l.call(e,r);if("string"==typeof t&&""!==t)return{text:s(t),formatted:!0}}catch{}return{text:s(String(a)),formatted:!1}}function Pi(e,t,i){const n=String(t??"").trim(),r=String(i??"").trim()||n;if(!r)return e.text;const o=e.formatted&&n?function(e,t){if(!t)return e;const i=e.replace(/\s+$/,"");return i.endsWith(t)?i.slice(0,i.length-t.length).replace(/\s+$/,""):e}(e.text,n):e.text;return`${o} ${r}`}function Fi(e,t){const i=(t?.entity||"").trim();if(!i)return"";const n=e?.states?.[i],r=n?.state;if(!n||null==r||""===r||"unavailable"===r||"unknown"===r)return Ri;const o=(t?.attr||"").trim(),s=Ai(e,i,o||null);if(null===s)return Ri;return Pi(s,o?"":String(n.attributes?.unit_of_measurement??"").trim(),t?.unit)}function Oi(e,t,i,n=()=>!0){const r=e??"";let o=!1;const s=r.replace(/\{([^{}\r\n]+)\}/g,(e,t)=>{const r=Di(t);return r?(o=!0,n(r.entity||"")?Fi(i,r):Ri):e});if(o)return s;const a=(t?.entity||"").trim();if(!a)return r;if(!n(a)){const e=r.indexOf(Ti);return e>=0?r.slice(0,e)+Ri+r.slice(e+2):r?`${r} ${Ri}`:Ri}const l=Fi(i,t),c=r.indexOf(Ti);return c>=0?r.slice(0,c)+l+r.slice(c+2):r?`${r} ${l}`:l}const Ii=20;function Ei(e){const t=Number(e?.scale);if(Number.isFinite(t)&&t>0)return Math.min(20,Math.max(.15,t));return{s:.7,m:1,l:1.5}[String(e?.size??"")]??1}function Ni(e,t){if(!t)return e;let i=e;for(const[e,n]of Object.entries(t))i=i.split("{"+e+"}").join(String(n));return i}const Hi="#55606c",Li=.55,Bi=20,qi=25,Wi={c:"#607d8b",a:.18};function ji(e,t=Wi){const i=e&&"object"==typeof e?e:null,n=i?.a;return{c:Dt(i?.c,t.c),a:"number"==typeof n&&Number.isFinite(n)?Math.min(1,Math.max(0,n)):t.a}}function Ui(e,t){const i=ji(e),n=t?.settings?.custom_fill;return n&&"object"==typeof n?ji(n,i):i}function Vi(e){const t=e?.settings||{},i=!e?.plan_url,n="glow"===t.fill_mode;return{showBorders:t.show_borders??i,showNames:t.show_names??i,color:Dt(t.room_color,Hi),opacity:"number"==typeof t.room_opacity?Math.min(1,Math.max(0,t.room_opacity)):.55,fill:["lqi","light","temp","custom"].includes(t.fill_mode)?t.fill_mode:"none",customFill:ji(t.custom_fill),glow:"boolean"==typeof t.glow_enabled?t.glow_enabled:n,tempMin:"number"==typeof t.temp_min?t.temp_min:20,tempMax:"number"==typeof t.temp_max?t.temp_max:25,showLqi:"boolean"==typeof t.show_lqi?t.show_lqi:null,cardFontScale:"number"==typeof t.card_font_scale&&t.card_font_scale>0?Math.min(3,Math.max(.5,t.card_font_scale)):1,labelTemp:!0===t.label_temp,labelHum:!0===t.label_hum,labelLqi:!0===t.label_lqi,labelLight:!0===t.label_light,bgColor:Dt(t.bg_color,null),hideDecor:!0===t.hide_decor,hideOpenings:!0===t.hide_openings}}function Gi(e,t){if(t.bgColor)return t.bgColor;const i=e?.bg_color;return Dt(i,"")}function Ki(e){return!1!==e?.show_room_tooltip}const Yi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}};function Xi(e){const t={},i=e?.fill_colors||{};for(const e of Object.keys(Yi)){const n=Yi[e],r=i[e];t[e]={c:Dt(r?.c,n.c),a:r&&"number"==typeof r.a?Math.min(1,Math.max(0,r.a)):n.a}}return t}function Zi(e,t,i){const n=Math.min(1,Math.max(0,i)),r=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),s=r.map((e,t)=>Math.round(e+(o[t]-e)*n));return"#"+s.map(e=>e.toString(16).padStart(2,"0")).join("")}function Ji(e,t,i,n,r,o,s,a=Wi){const l=function(e,t,i,n,r,o,s,a=Wi){if("custom"===e)return ji(a);if("lqi"===e){if(null==t)return null;const e=(t-40)/140;return{c:Zi(s.lqi_low.c,s.lqi_high.c,e),a:s.lqi_low.a+(s.lqi_high.a-s.lqi_low.a)*Math.min(1,Math.max(0,e))}}if("light"===e)return"none"===i?s.light_none.a>0?s.light_none:null:"on"===i?s.light_on:s.light_off;if("temp"===e){if(null==n)return null;const e=Math.min(r,o),t=Math.max(r,o);return nt?s.temp_hot:s.temp_ok}return null}(e,t,i,n,r,o,s,a);return l?{color:l.c,opacity:l.a,mode:e}:null}const Qi={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},en=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function tn(e){for(const t of[...Object.values(Qi),...en])if(e===t[0]||e===t[1])return t;return null}function nn(e,t,i,n,r){if(!n||"unavailable"===n||"unknown"===n)return e;if(r){const i="cover"===t?tn(e):null;return i?"closed"===n?i[0]:i[1]:e}if("binary_sensor"===t){if("door"===i)return"on"===n?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===n?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===n?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===t){const t=Qi[String(i||"")];if(t)return"closed"===n?t[0]:t[1];const r=tn(e);return r?"closed"===n?r[0]:r[1]:e}return"lock"===t?"locked"===n?"mdi:lock":"mdi:lock-open-variant":"light"===t&&"mdi:lightbulb"===e&&"on"===n?"mdi:lightbulb-on":e}function rn(e){const t=Math.min(4e4,Math.max(1e3,e))/100,i=t<=66?255:329.698727446*Math.pow(t-60,-.1332047592),n=t<=66?99.4708025861*Math.log(t)-161.1195681661:288.1221695283*Math.pow(t-60,-.0755148492),r=t>=66?255:t<=19?0:138.5177312231*Math.log(t-10)-305.0447927307,o=e=>Math.round(Math.min(255,Math.max(0,e)));return[o(i),o(n),o(r)]}const on=1/2.2;function sn(e){const t=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(e);return t?"#"+t.slice(1).map(e=>Math.min(255,Number(e)).toString(16).padStart(2,"0")).join(""):null}function an(e){if(!e||"object"!=typeof e||Array.isArray(e))return null;const t=e;if(Object.keys(t).some(e=>"c"!==e&&"bri"!==e))return null;const i=Dt(t.c,null);return i?void 0===t.bri||null===t.bri?{c:i}:"number"!=typeof t.bri||!Number.isFinite(t.bri)||t.bri<.01||t.bri>1?null:{c:i,bri:t.bri}:null}function ln(e,t,i){const n=an(t),r=e?.attributes||{},o=n?.bri??function(e){const t=e?.attributes?.brightness,i="number"==typeof t?t:"string"==typeof t&&""!==t.trim()?Number(t):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(e);if(n)return{c:n.c,bri:o};const s=Pt(r.rgb_color);if(s)return{c:sn(s)||Dt(i,"#ffd9a0"),bri:o};const a=Number(r.color_temp_kelvin)||(Number(r.color_temp)>0?1e6/Number(r.color_temp):NaN);return Number.isFinite(a)&&a>0?{c:(l=rn(a),"#"+l.slice(0,3).map(e=>Math.min(255,Math.max(0,Math.round(Number(e)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Dt(i,"#ffd9a0"),bri:o};var l}function cn(e,t,i){return"on"===e?.state?ln(e,t,i):null}function hn(e,t=1){const i=Math.max(0,Math.min(1,Number.isFinite(e)?e:1)),n=.7*Math.max(0,Math.min(1,Number.isFinite(t)?t:1))*(.4+.6*Math.pow(i,on));return Math.max(0,Math.min(1,n))}function dn(e){return e.startsWith("light.")||e.startsWith("switch.")}function un(e,t,i=1e-6){const n=[];if(!e||!t||e.length<3||t.length<3)return n;for(let r=0;ru||l>u)continue;const p=(r[0]-o[0])*h+(r[1]-o[1])*d,m=(s[0]-o[0])*h+(s[1]-o[1])*d,_=Math.max(0,Math.min(p,m)),g=Math.min(c,Math.max(p,m));g-_>i&&n.push([o[0]+h*_,o[1]+d*_,o[0]+h*g,o[1]+d*g])}}return n}function pn(e,t,i=1e-6){const n=[];for(const r of e){const e=[r[0],r[1]],o=[r[2],r[3]],s=o[0]-e[0],a=o[1]-e[1],l=Math.hypot(s,a);if(lo||r>o)continue;const s=(n[0]-e[0])*c+(n[1]-e[1])*h,a=(n[2]-e[0])*c+(n[3]-e[1])*h,u=Math.max(0,Math.min(s,a)),p=Math.min(l,Math.max(s,a));p-u>i&&d.push([u,p])}if(!d.length){n.push([e[0],e[1],o[0],o[1]]);continue}d.sort((e,t)=>e[0]-t[0]);let u=0;for(const[t,r]of d)t-u>i&&n.push([e[0]+c*u,e[1]+h*u,e[0]+c*t,e[1]+h*t]),u=Math.max(u,r);l-u>i&&n.push([e[0]+c*u,e[1]+h*u,o[0],o[1]])}return n}function mn(e,t,i=1e-6){const n=[];for(let t=0;t{if("string"!=typeof e||!e)return;const i=vn(e);i.startsWith("/api/houseplan/content/")&&t.add(i)};for(const t of e?.spaces||[]){i(t?.plan_url);for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url)}for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url);return t}function vn(e){return e?e.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+e.slice(23):e.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+e.slice(23):e:""}function yn(e,t){const i=t?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:e}function bn(e,t){const i=t?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||e}function wn(e,t,i,n){if(!t||!i||t===i)return e;const r="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(r))return e;const t=e.url.split(r)[1]||"",[i,s]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(n){const t=n[decodeURIComponent(i)]??n[i];return t?{...e,url:e.url.split(r+i)[0]+o+encodeURIComponent(t)+s}:e}return{...e,url:e.url.split(r).join(o)}})}function kn(e,t=1){const i=Number(e);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):t}function xn(e,t,i){let n=null,r=null;for(const o of t){if(!(Math.abs(o[0]-e[0])<1e-6&&Math.abs(o[1]-e[1])<1e-6)){if(Math.abs(o[0]-e[0])<=i){const t=Math.abs(o[1]-e[1]);t>1e-6&&(!n||t1e-6&&(!r||t"number"==typeof e&&Number.isFinite(e),In=(e,t,i)=>Math.min(i,Math.max(t,e));function En(e){return e.elevation<=-6?"night":e.elevation>=6?"day":e.rising?"dawn":"dusk"}function Nn(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;return t>=300&&t<480?"dawn":t>=480&&t<1080?"day":t>=1080&&t<1260?"dusk":"night"}function Hn(e){const t=En(e),i=e.azimuth*Math.PI/180;return{sunX:50-42*Math.sin(i),sunY:78-In(e.elevation,0,90)/90*64,sunOpacity:"night"===t?0:In((e.elevation+6)/12,0,1)}}function Ln(e){const t=(Math.floor(Number(e)||0)%1440+1440)%1440;if(t<300||t>=1260)return{sunX:50,sunY:78,sunOpacity:0};const i=(t-300)/960,n=(t-300)/120,r=(1260-t)/120;return{sunX:8+84*i,sunY:78-64*Math.sin(i*Math.PI),sunOpacity:Math.max(.18,Math.min(n,r,1))}}function Bn(e,t=new Date){const i=function(e){const t=e?.states?.["sun.sun"]?.attributes;return t&&On(t.azimuth)&&On(t.elevation)&&"boolean"==typeof t.rising?{azimuth:An(t.azimuth),elevation:t.elevation,rising:t.rising}:null}(e);if(i)return{phase:En(i),source:"sun",...Hn(i)};const n="number"==typeof t?t:function(e=new Date){return 60*e.getHours()+e.getMinutes()}(t);return{phase:Nn(n),source:"clock",...Ln(n)}}function qn(e){return`${e.source}|${e.phase}|${e.sunX.toFixed(2)}|${e.sunY.toFixed(2)}|`+e.sunOpacity.toFixed(3)}const Wn=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],jn=e=>Math.min(1,Math.max(0,e));function Un(e,t,i=6){const n=e.angle*Math.PI/180,r=[Math.sin(n),-Math.cos(n)],o=n=>{const o=[e.x+r[0]*i*n,e.y+r[1]*i*n];return t.find(e=>e.poly.length>=3&&Zt(o,e.poly))||null},s=o(1),a=o(-1);return s&&a?null:s||a?s?{normal:[-r[0],-r[1]],roomId:s.id}:{normal:r,roomId:a.id}:null}function Vn(e,t,i){return i>0&&e[0]*t[0]+e[1]*t[1]>.05}function Gn(e,t,i,n){return[[e[0],e[1]],[t[0],t[1]],[t[0]+i[0]*n,t[1]+i[1]*n],[e[0]+i[0]*n,e[1]+i[1]*n]]}function Kn(e,t){try{const i=Ct([[...e.map(e=>[e[0],e[1]]),[e[0][0],e[0][1]]]],[[...t.map(e=>[e[0],e[1]]),[t[0][0],t[0][1]]]]),n=[];for(const e of i){const t=e?.[0];!Array.isArray(t)||t.length<4||n.push(t.slice(0,t.length-1).map(e=>[e[0],e[1]]))}return n}catch{return[]}}function Yn(e,t,i,n,r,o,s){if(!(n>0))return[];const a=Pn(i,r),l=[-a[0],-a[1]],c=function(e){const t=Math.min(90,Math.max(0,e));return.7*(.8+1.7*Math.pow(1-t/90,1.6))}(n),h=[];for(const i of t){if(!(i.length>0))continue;const t=Un(i,e);if(!t||!Vn(t.normal,a,n))continue;const r=e.find(e=>e.id===t.roomId);if(!r)continue;const d=o&&o[t.roomId]||r.poly,u=i.angle*Math.PI/180,p=i.length/2,m=[-t.normal[0],-t.normal[1]],_=Math.max(0,s?.[i.id]||0),g=i.x+m[0]*_/2,f=i.y+m[1]*_/2,v=Math.cos(u)*p,y=Math.sin(u)*p,b=[g-v,f-y],w=[g+v,f+y],k=c*i.length,x=Kn(Gn(b,w,l,k),d);if(!x.length)continue;const $=l[0]*m[0]+l[1]*m[1];h.push({openingId:i.id,roomId:t.roomId,polys:x,a:b,b:w,dir:l,len:k,normal:m,depth:k*$})}return h}const Xn=e=>"number"==typeof e&&Number.isInteger(e)&&e>=0&&e<=359?e:null;function Zn(e,t){const i=Xn(t?.north_deg);return null!==i?i:Xn(e?.north_deg)}function Jn(e,t){const i=e=>"static"===e||"daynight"===e?e:null;return i(t?.bg_mode)??i(e?.bg_mode)??"static"}function Qn(e,t){const i=t?.sun_rays;return"boolean"==typeof i?i:!0===e?.sun_rays}function er(e){const t=e?.states?.["sun.sun"]?.attributes,i=Number(t?.azimuth),n=Number(t?.elevation);return Number.isFinite(i)&&Number.isFinite(n)?{azimuth:i,elevation:n}:null}const tr=["dawn","day","dusk","night"];function ir(e){if(!e)return"";const t=Fn[e.phase];return[`--hp-day-cycle-outline-near:${t.outlineNear}`,`--hp-day-cycle-outline-mid:${t.outlineMid}`,`--hp-day-cycle-outline-far:${t.outlineFar}`].join(";")}function nr(e,t=1){if(!e)return V;const i=[`--hp-day-cycle-sun-x:${e.sunX.toFixed(2)}%`,`--hp-day-cycle-sun-y:${e.sunY.toFixed(2)}%`,`--hp-day-cycle-sun-opacity:${e.sunOpacity.toFixed(3)}`,`opacity:${Math.min(1,Math.max(0,t)).toFixed(4)}`].join(";");return W``}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}render(){if(!this._config||!this.hass)return V;const e=uu(this,fu,xu(this.hass,this._config.language));if("cold"===e)return pu();if("warm"===e)return U;const t=this._snap?.config;if(!t)return this._errorCard(Su(this._lang,"space_card.loading"));const i=this._config.space,n=Da(t).find(e=>e.id===i),r=void 0!==this._config.title?this._config.title:n?.title||"",o=this._renderDeviceSnapshot,s=Qm({hass:o?.hass||this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:i,iconSize:this._config.icon_size,fit:this._config.fit,compactTopFrame:""===this._config.title||null===this._config.title,stageWidth:this._stageWidth,lang:this._lang,displayUrl:e=>this._signer.display(this.hass,e),decorAssetUrl:e=>this._decorAssets.get(e)?.url||"",assetLoaded:this._onAssetLoaded,registry:o?{revision:o.sourceSequence,authoritative:!1,access:"limited",entities:{...o.hass.entities},devices:{...o.hass.devices},lastSuccess:o.capturedAt}:mh(this.hass),devices:[...o?.devices||this._devices],areaRelocationIds:this._areaRelocationIds,presentations:o?.presentations,activityRuntime:this._activityRuntime,reducedMotion:this._reducedMotion,virtualLights:this._snap?.virtualLights,lightPools:!0===this._config.light_pools,glowRuntime:{state:this._glowRuntimeState,host:this._glowRuntimeHost,screenBlend:this._glowScreenBlend},liveStates:!1!==this._config.live_states,showTemperature:!1!==this._config.show_temperature,showSignal:!1!==this._config.show_signal,inert:this._continuity.overlayBlocksInteraction});if(!s)return this._errorCard(Su(this._lang,"space_card.not_found",{id:i}));const a=!1!==this._config.show_button,l=this._config.button_label||Su(this._lang,"space_card.button"),c=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return W` ${l}
    `:V} - `}}w_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},w_.styles=[su,o` + `}}x_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},x_.styles=[lu,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -4765,7 +4765,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",w_),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-space-card"===e.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only live schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const k_=o` + `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",x_),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-space-card"===e.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only live schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const S_=o` :host { --hp-editor-tray-bg-fallback: color-mix(in srgb, var(--card-background-color, var(--hp-bg)) 94%, var(--hp-line)); --hp-editor-tray-bg: color-mix(in srgb, var(--card-background-color, var(--hp-bg)) 86%, transparent); @@ -4936,7 +4936,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef .editor-secondary { transition: none; } .editor-group-launcher .group-chevron { transition: none; } } -`;function x_(e,t){return e.catch(()=>{}).then(t)}function $_(e,t,i,n,r){return{previous:JSON.parse(JSON.stringify(e)),previousFingerprint:i,revision:n,attempted:t,attemptedFingerprint:r(t)}}function S_(e,t,i){const n=e._serverCfg;return!(!n||e._cfgRev!==t.revision||i(n)!==t.attemptedFingerprint)&&(e._serverCfg=t.previous,e._cfgContentFingerprint=t.previousFingerprint,e.requestUpdate(),!0)}class M_{constructor(e=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(e))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(e){this._undo.push(e),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const e=this._undo.pop()??null;return e&&this._redo.push(e),e}redo(){const e=this._redo.pop()??null;return e&&this._undo.push(e),e}removeWhere(e){const t=this._undo.length,i=this._redo.length;return this._undo=this._undo.filter(t=>!e(t)),this._redo=this._redo.filter(t=>!e(t)),t+i-this._undo.length-this._redo.length}clear(){this._undo=[],this._redo=[]}}function C_(e,t){const i=e[t];return i&&Number.isFinite(i.x)&&Number.isFinite(i.y)?{x:i.x,y:i.y,..."string"==typeof i.s?{s:i.s}:{}}:null}function R_(e,t,i){if(null===i){if(!(t in e))return e;const i={...e};return delete i[t],i}const n={...e[t]||{}};delete n.x,delete n.y,delete n.s;const r={...n,x:i.x,y:i.y,...void 0!==i.s?{s:i.s}:{}};return{...e,[t]:r}}const T_=Object.freeze({enabled:!1,z2mBaseTopics:Object.freeze([])});function D_(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^\/+|\/+$/g,"").replace(/\/{2,}/g,"/");return!t||t.length>180||/[#+\u0000-\u001f\u007f]/.test(t)?null:t}function z_(e){const t=e?.zigbee_topology;if(!t||"object"!=typeof t)return T_;const i=t,n=Array.isArray(i.z2m_base_topics)?i.z2m_base_topics:Array.isArray(i.z2mBaseTopics)?i.z2mBaseTopics:[],r=new Set,o=[];for(const e of n){const t=D_(e);if(t&&!r.has(t)&&(r.add(t),o.push(t),8===o.length))break}return{enabled:!0===i.enabled,z2mBaseTopics:o}}function A_(e,t){const i={...e},n=t.z2mBaseTopics.map(D_).filter(Boolean);return t.enabled||n.length?i.zigbee_topology={enabled:!0===t.enabled,z2m_base_topics:[...new Set(n)].slice(0,8)}:delete i.zigbee_topology,i}const P_={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},F_=e=>Math.max(0,Math.min(1,e)),O_=(e,t,i)=>e+(t-e)*i,I_=(e,t,i)=>{const n=1-e;return 3*n*n*e*t+3*n*e*e*i+e*e*e},E_=e=>{const t=F_(e);if(0===t||1===t)return t;let i=0,n=1,r=t;for(let e=0;e<12;e++)r=(i+n)/2,I_(r,.2,.2)0&&e.w>0?t/e.w:1,viewBox:{...e}}}function H_(e,t,i){const n=Math.max(1e-9,e.pixelsPerUnit),r=Math.max(1e-9,t/n),o=Math.max(1e-9,i/n);return{x:e.centerX-r/2,y:e.centerY-o/2,w:r,h:o}}function L_(e){const t=e.trim(),i=/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/i.exec(t);if(i)return[Number(i[1]),Number(i[2]),Number(i[3]),null==i[4]?1:Number(i[4])];const n=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return n?[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16),8===n.length?parseInt(n.slice(6,8),16)/255:1]:null}function B_(e,t,i){const n=L_(e),r=L_(t);if(!n||!r)return i<.5?e:t;const o=F_(i);return`rgba(${Math.round(O_(n[0],r[0],o))}, ${Math.round(O_(n[1],r[1],o))}, ${Math.round(O_(n[2],r[2],o))}, ${O_(n[3],r[3],o).toFixed(3)})`}class q_{constructor(e,t=P_){this._hooks=e,this._clock=t,this._token=0,this._raf=0,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase||"preparing"===this._state?.phase}get presented(){return this._state?.presented||null}start(e,t,i,n=220){this.cancel(!1);const r=++this._token,o=this._clock.now();if(this._state={token:r,phase:n<=0?"settling":"running",from:e,to:t,presented:n<=0?t:e,startedAt:o,duration:Math.max(0,n),targetMode:i},n<=0)return this._hooks.frame(this._state),this._hooks.settled(this._state),this._state=null,r;this._hooks.frame(this._state);const s=e=>{const t=this._state;if(!t||t.token!==r)return;const i=F_((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:function(e,t,i){const n=E_(i),r=O_(e.stageWidth,t.stageWidth,n),o=O_(e.stageHeight,t.stageHeight,n),s=Math.max(1e-9,e.viewport.pixelsPerUnit),a=Math.max(1e-9,t.viewport.pixelsPerUnit),l={centerX:O_(e.viewport.centerX,t.viewport.centerX,n),centerY:O_(e.viewport.centerY,t.viewport.centerY,n),pixelsPerUnit:Math.exp(O_(Math.log(s),Math.log(a),n))};return{presentedMode:n<.5?e.presentedMode:t.presentedMode,editorChromeHeight:O_(e.editorChromeHeight,t.editorChromeHeight,n),stageWidth:r,stageHeight:o,viewport:{...l,viewBox:H_(l,r,o)},stageColor:B_(e.stageColor,t.stageColor,n),paperColor:B_(e.paperColor,t.paperColor,n),sceneBrightness:O_(e.sceneBrightness,t.sceneBrightness,n),architectureOpacity:O_(e.architectureOpacity,t.architectureOpacity,n),backdropOpacity:O_(e.backdropOpacity,t.backdropOpacity,n),viewWeight:O_(e.viewWeight,t.viewWeight,n),editorWeight:O_(e.editorWeight,t.editorWeight,n),toolbarContentOpacity:O_(e.toolbarContentOpacity,t.toolbarContentOpacity,n)}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(s):(this._raf=0,this._hooks.settled(t),this._state?.token===r&&(this._state=null))};return this._raf=this._clock.requestFrame(s),r}cancel(e=!0){this._raf&&this._clock.cancelFrame(this._raf),this._raf=0;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=t.to,this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}}const W_={now:()=>"undefined"!=typeof performance?performance.now():Date.now(),requestFrame:e=>"function"==typeof requestAnimationFrame?requestAnimationFrame(e):null,cancelFrame:e=>{"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(e)}},j_=e=>Number.isFinite(e);function U_(e){return!!e&&j_(e.zoom)&&e.zoom>0&&j_(e.viewBox.x)&&j_(e.viewBox.y)&&j_(e.viewBox.w)&&e.viewBox.w>0&&j_(e.viewBox.h)&&e.viewBox.h>0}function V_(e){return{zoom:e.zoom,viewBox:{...e.viewBox}}}function G_(e,t){return Math.abs(e.zoom-t.zoom)<1e-9&&Math.abs(e.viewBox.x-t.viewBox.x)<1e-6&&Math.abs(e.viewBox.y-t.viewBox.y)<1e-6&&Math.abs(e.viewBox.w-t.viewBox.w)<1e-6&&Math.abs(e.viewBox.h-t.viewBox.h)<1e-6}const K_=(e,t,i)=>e+(t-e)*i,Y_=(e,t,i)=>Math.exp(K_(Math.log(e),Math.log(t),i));class X_{constructor(e,t=W_){this._hooks=e,this._clock=t,this._token=0,this._raf=null,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase}get presented(){return this._state?.presented||null}get target(){return this._state?.to||null}start(e,t,i,n){this.cancel(!1);const r=++this._token,o=V_(e),s=V_(t),a=n>0&&U_(o)&&U_(s),l=this._clock.now(),c={token:r,phase:a?"running":"settling",from:o,to:s,presented:V_(a?o:s),startedAt:l,duration:a?n:0,reason:i};if(this._state=c,this._hooks.frame(c),!a)return this._settle(r),r;const h=e=>{const t=this._state;if(!t||t.token!==r)return;const i=Math.max(0,Math.min(1,(e-t.startedAt)/t.duration));if(t.presented=i>=1?V_(t.to):function(e,t,i){if(!U_(e))return V_(t);if(!U_(t))return V_(e);const n=E_(i);if(n<=0)return V_(e);if(n>=1)return V_(t);const r=e.viewBox.x+e.viewBox.w/2,o=e.viewBox.y+e.viewBox.h/2,s=t.viewBox.x+t.viewBox.w/2,a=t.viewBox.y+t.viewBox.h/2,l=Y_(e.viewBox.w,t.viewBox.w,n),c=Y_(e.viewBox.h,t.viewBox.h,n),h=K_(r,s,n),d=K_(o,a,n);return{zoom:Y_(e.zoom,t.zoom,n),viewBox:{x:h-l/2,y:d-c/2,w:l,h:c}}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i>=1)return this._raf=null,void this._settle(r);this._raf=this._clock.requestFrame(h),null===this._raf&&this._settle(r)};return this._raf=this._clock.requestFrame(h),null===this._raf&&(c.phase="settling",c.presented=V_(s),this._hooks.frame(c),this._settle(r)),r}cancel(e=!0){null!==this._raf&&this._clock.cancelFrame(this._raf),this._raf=null;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=V_(t.to),this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}_settle(e){const t=this._state;t&&t.token===e&&(t.phase="settling",t.presented=V_(t.to),this._hooks.settled(t),this._state?.token===e&&(this._state=null))}}const Z_=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])}),J_=64;function Q_(e){return[e.rotDeg,e.tiltDeg,e.xyScale,e.zScale,e.origin[0],e.origin[1]].every(Number.isFinite)&&Math.abs(e.xyScale)>1e-12&&Math.abs(Math.cos(e.tiltDeg*Math.PI/180))>1e-12}function eg(e,t,i=Z_){if(!(Q_(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const n=i.rotDeg*Math.PI/180,r=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],s=e[1]-i.origin[1],a=(o*Math.cos(n)-s*Math.sin(n))*i.xyScale,l=(o*Math.sin(n)+s*Math.cos(n))*i.xyScale;return[i.origin[0]+a,i.origin[1]+l*Math.cos(r)-t*i.zScale*Math.sin(r)]}function tg(e,t=Z_){if(!Q_(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,n=t.tiltDeg*Math.PI/180,r=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(n));return[t.origin[0]+r*Math.cos(i)+o*Math.sin(i),t.origin[1]-r*Math.sin(i)+o*Math.cos(i)]}function ig(e=Z_){return`matrix(${function(e=Z_){if(!Q_(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,n=e.xyScale*Math.cos(t),r=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),s=e.xyScale*Math.cos(t)*Math.cos(i);return[n,o,r,s,e.origin[0]-n*e.origin[0]-r*e.origin[1],e.origin[1]-o*e.origin[0]-s*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function ng(e,t=Z_){const{rect:i,wallHeight:n}=e,r=e.openingHeight??n,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(o)||n<0||r<0||o<0)throw new Error("invalid isometric frame");const s=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],a=Math.max(n,r),l=s.flatMap(e=>[eg(e,-o,t),eg(e,a,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),u=Math.min(...h);return{x:d,y:u,w:Math.max(...c)-d,h:Math.max(...h)-u}}const rg=[".dev",".vacpuck",".oplock",".op-hit",".opening",".rlgo","a","button","input","select","textarea",'[contenteditable]:not([contenteditable="false"])',"[data-room-fit-block]",'[role="link"]','[role="button"]'].join(","),og=(e,t)=>{try{return"function"==typeof e?.matches&&e.matches(t)}catch{return!1}};function sg(e){for(const t of e){const e=t;if(og(e,".roomlabel[data-id]")){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(og(e,rg))return{kind:"interactive"};if(og(e,'[data-hp="room"][data-id]')){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(og(e,".stage"))return{kind:"background"}}return{kind:"outside"}}function ag(e){const t=sg(e);return"room"===t.kind?t.roomId:null}class lg{constructor(){this.pointer=null,this.sequence=null}clear(){this.pointer=null,this.sequence=null}clearOutside(e){let t={kind:"outside"};try{t=sg(e.composedPath())}catch{}"outside"===t.kind&&this.clear()}pointerDown(e,t,i){let n={kind:"outside"};try{n=sg(e.composedPath())}catch{}this.pointer=function(e){const t="mouse"===(i=e.pointerType)||"touch"===i||"pen"===i?i:null;var i;return t&&"view"===e.mode&&"background"===e.owner.kind&&!e.blocked&&e.isPrimary&&0===e.button&&Number.isFinite(e.x)&&Number.isFinite(e.y)?{pointerId:e.pointerId,spaceId:e.spaceId,modality:t,x:e.x,y:e.y}:null}({pointerId:e.pointerId,pointerType:e.pointerType,isPrimary:e.isPrimary,button:e.button,spaceId:t,mode:i?"view":"blocked",owner:n,blocked:!i,x:e.clientX,y:e.clientY}),this.pointer||(this.sequence=null)}pointerUp(e,t,i,n,r=Date.now()){let o={kind:"outside"};try{o=sg(e.composedPath())}catch{}const s=function(e,t,i){if(!t||"view"!==i.mode||"background"!==i.owner.kind||i.blocked||t.pointerId!==i.pointerId||t.spaceId!==i.spaceId||!Number.isFinite(i.x)||!Number.isFinite(i.y)||!Number.isFinite(i.now)||Math.abs(i.x-t.x)+Math.abs(i.y-t.y)>=8)return{sequence:null,trigger:!1};const n=e?i.now-e.at:Number.POSITIVE_INFINITY;return e&&e.spaceId===i.spaceId&&e.modality===t.modality&&n>=0&&n<=350?{sequence:null,trigger:!0}:{sequence:{at:i.now,spaceId:i.spaceId,modality:t.modality},trigger:!1}}(this.sequence,this.pointer,{pointerId:e.pointerId,spaceId:t,mode:i?"view":"blocked",owner:o,blocked:n||!i,x:e.clientX,y:e.clientY,now:r});return this.pointer=null,this.sequence=s.sequence,s.trigger}}const cg=e=>e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function hg(e){return!!e&&Number.isFinite(e.x)&&Number.isFinite(e.y)&&Number.isFinite(e.w)&&e.w>0&&Number.isFinite(e.h)&&e.h>0}function dg(e,t=0){if(!e.length||!Number.isFinite(t)||t<0||!e.every(cg))return null;let i=1/0,n=1/0,r=-1/0,o=-1/0;for(const t of e)i=Math.min(i,t[0]),n=Math.min(n,t[1]),r=Math.max(r,t[0]),o=Math.max(o,t[1]);const s=r-i,a=o-n;return s>0&&a>0?{x:i-t,y:n-t,w:s+2*t,h:a+2*t}:null}function ug(e,t){return`${e("editor.load_failed")} ${e(t.terminal?"editor.refresh_advice":"editor.retry_advice")}`}class pg extends Error{}class mg{constructor(e){this.options=e,this._state="idle",this._inFlight=null}get state(){return this._state}ensure(){return"ready"===this._state?Promise.resolve(!0):"failed"===this._state?Promise.resolve(!1):(this._inFlight||(this._setState("loading"),this._inFlight=this._loadWithRetry().finally(()=>{this._inFlight=null})),this._inFlight)}async _loadWithRetry(){let e=new Error("Editor runtime did not load"),t=!1;for(const i of[0,1])try{const e=await this.options.load(i);if(e.fingerprint!==this.options.expectedFingerprint)throw new pg(`Editor runtime fingerprint mismatch: expected ${this.options.expectedFingerprint}, got ${e.fingerprint}`);const t=e.create();return this.options.install(t),this._setState("ready"),!0}catch(i){e=i,i instanceof pg&&(t=!0)}return this._setState(t?"failed":"idle"),this.options.failed?.(e,{terminal:t}),!1}_setState(e){e!==this._state&&(this._state=e,this.options.stateChanged?.(e))}}const _g=(e,t)=>{if(!e.has(t))return{hit:!1};const i=e.get(t);return e.delete(t),e.set(t,i),{hit:!0,value:i}},gg=(e,t,i,n)=>{for(e.delete(t),e.set(t,i);e.size>n;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},fg=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),vg=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`,yg="houseplan_card_alpha_v1",bg=Object.freeze([Object.freeze({id:"iso",issue:89,summary:"Volumetric plan renderer"})]);function wg(e){return/^[a-z][a-z0-9-]*$/.test(e.id)&&Number.isInteger(e.issue)&&e.issue>0&&!!e.summary.trim()}function kg(e){const t=String(e||"").replace(/^#/,"");return new URLSearchParams(t)}function xg(e){return kg(e).get("space")||""}function $g(e,t,i=bg){const n=function(e){const t=new Set;for(const i of e){if(!wg(i)||t.has(i.id))return!1;t.add(i.id)}return!0}(i),r=function(e){let t,i=!1;const n=e=>{for(const n of e.getAll("hp_alpha"))i=!0,"1"===n?t=!0:"0"===n&&(t=!1)};return n(new URLSearchParams(String(e.search||"").replace(/^\?/,""))),n(kg(e.hash)),{present:i,value:t}}(e),o=void 0!==r.value,s="1"===t,a=o?!0===r.value:!r.present&&s,l=n&&a;return{alpha:l,active:Object.freeze(l?i.map(e=>e.id).sort():[]),space:xg(e.hash),persist:n&&o?l?"1":"0":void 0,knownUrlOperation:o}}let Sg={alpha:!1,active:Object.freeze([]),space:""},Mg=!1,Cg="";const Rg=new Set;function Tg(){if("undefined"==typeof window)return Sg;const e=$g(window.location,function(){try{return window.localStorage.getItem(yg)}catch{return null}}());void 0!==e.persist&&function(e){try{window.localStorage.setItem(yg,e)}catch{}}(e.persist),Sg={alpha:e.alpha,active:e.active,space:e.space},window.__hpAlpha=e.alpha,window.__hpLabs=e.active;for(const e of Rg)e(Sg);return Sg}function Dg(){Tg()}function zg(){if("undefined"==typeof window)return Sg;if(!Mg){Mg=!0,window.__hpLabsListenerCleanup?.(),window.addEventListener("hashchange",Dg),window.addEventListener("popstate",Dg);const e=()=>{window.removeEventListener("hashchange",Dg),window.removeEventListener("popstate",Dg),window.__hpLabsListenerCleanup===e&&delete window.__hpLabsListenerCleanup};window.__hpLabsListenerCleanup=e}return Tg()}const Ag=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Pg(e){let t=0;for(let i=0;iArray.isArray(e)&&Number.isFinite(e[0])&&Number.isFinite(e[1])).map(e=>[Number(e[0]),Number(e[1])]),i=t[t.length-1];return t.length>1&&t[0][0]===i[0]&&t[0][1]===i[1]&&t.pop(),t}(e);if(i.length<3)return[];const n=!t;return Pg(i)>0===n?i:[...i].reverse()}function Og(e,t,i){return e.length?`M ${e.map(e=>Ag(eg(e,i,t))).join(" L ")} Z`:""}function Ig(e,t,i){const n=t[1]-e[1],r=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return n*Math.sin(o)+r*Math.cos(o)}function Eg(e,t,i=Z_){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const n=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=Fg(e,t);if(!i.length)return i;let n=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(Pg(e))>=1e-9)).filter(e=>e.length&&Pg(e[0])>0);n.sort((e,t)=>{const i=e[0][0],n=t[0][0];return i[0]-n[0]||i[1]-n[1]||Math.abs(Pg(t[0]))-Math.abs(Pg(e[0]))});const r=[],o=[];let s=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:r.join(" "),sides:o,componentCount:n.length,edgeCount:s}}function Ng(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const Hg=e=>e.every(Number.isFinite);function Lg(e,t,i,n){const r=e[0]*i,o=e[1]*n,s=t*Math.PI/180;return[r*Math.cos(s)-o*Math.sin(s),r*Math.sin(s)+o*Math.cos(s)]}function Bg(e,t,i,n,r,o,s){const a=e.flipH?-1:1,l="gate"===e.type?1:e.flipV?-1:1,c=(e.type,e.flipV,e.angle,e.face,{ox:0,oy:0}),h=function(e,t){return[e[0]+t[0],e[1]+t[1]]}([e.x+c.ox,e.y+c.oy],Lg(i,e.angle,a,l));return{leaf:t,hinge:h,closedVector:Lg(n,e.angle,a,l),quarterVector:Lg([-n[1],n[0]],e.angle,a,l),turnDeg:r,bottom:o,top:s}}const qg=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Wg(e,t,i=Z_){const n=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const r=function(e,t){const i=e.turnDeg*t*Math.PI/180;return[e.hinge[0]+e.closedVector[0]*Math.cos(i)+e.quarterVector[0]*Math.sin(i),e.hinge[1]+e.closedVector[1]*Math.cos(i)+e.quarterVector[1]*Math.sin(i)]}(t,n),o=eg(t.hinge,t.bottom,i),s=eg(r,t.bottom,i),a=eg(r,t.top,i),l=eg(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${qg(o)} L ${qg(s)} L ${qg(a)} L ${qg(l)} Z`,shadowD:`M ${qg(eg(t.hinge,0,i))} L ${qg(eg(r,0,i))}`,depth:Math.max(o[1],s[1])}})}const jg={window:120,door:90,passage:90,gate:300};function Ug(e){return jg[e]}function Vg(e,t){return{type:e,lengthCm:Ug(e),flipH:!1,flipV:!1,revision:t}}function Gg(e,t){const i=Math.max(0,e.renderedLength)/2,n=Math.max(0,e.target.physicalHalfWidth),r=n+.18*Math.max(0,t),o=e=>({x1:e,y1:-r,x2:e,y2:r});return{rect:{x:-i,y:n?-n:0,width:2*i,height:2*n},boundaries:[o(-i),o(i)]}}function Kg(e,t){const i=[e[0],e[1]],n=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,n)<=0?{a:i,b:n}:{a:n,b:i}}function Yg(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const n=Kg(e.a,e.b);if(Math.hypot(n.b[0]-n.a[0],n.b[1]-n.a[1])<=1e-9)return;const r=function(e,t){const i=e=>(Math.abs(e)<=5e-10?0:Math.round(1e6*e)/1e6).toFixed(6);return`${i(e[0])},${i(e[1])}>${i(t[0])},${i(t[1])}`}(n.a,n.b),o=t.get(r);if(o)return o.physicalHalfWidth=Math.max(o.physicalHalfWidth,e.half||0),o.sourceOrder=Math.min(o.sourceOrder,i),void(e.partitionHost&&(o.partitionHost&&o.partitionHost.id!==e.partitionHost.id?o.ambiguousPartitionHost=!0:o.partitionHost=e.partitionHost));t.set(r,{segmentKey:r,a:n.a,b:n.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function Xg(e,t){if(Math.abs(e.distance-t.distance)>1e-9)return e.distance-t.distance;if(Math.abs(e.perpendicular-t.perpendicular)>1e-9)return e.perpendicular-t.perpendicular;return(e.target.segmentKeyt.target.segmentKey?1:0)||e.target.sourceOrder-t.target.sourceOrder}function Zg(e){const t=Yg(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,a=(e[0]-t.a[0])*o+(e[1]-t.a[1])*s,l=Math.max(0,Math.min(r,a)),c=t.a[0]+l*o,h=t.a[1]+l*s;return{along:l,length:r,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*s-(e[1]-t.a[1])*o)}}(e.pointer,t);return{target:t,...i,envelope:Math.max(e.baseTolerance,t.physicalHalfWidth+e.bodyPointerPadding)}}).filter(e=>e.distance<=e.envelope+1e-9).filter(t=>!function(e,t,i,n,r){const o=t.b[0]-t.a[0],s=t.b[1]-t.a[1],a=Math.hypot(o,s);if(!(a>1e-9))return!1;const l=o/a,c=s/a;for(const o of i){if(!o.open&&o.kind)continue;const i=Kg(o.a,o.b),s=i.b[0]-i.a[0],a=i.b[1]-i.a[1],h=Math.hypot(s,a);if(!(h>1e-9))continue;const d=s/h,u=a/h;if(Math.abs(l*u-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>r)continue;const p=(e[0]-i.a[0])*d+(e[1]-i.a[1])*u,m=1e-9;if(!(p<=m||p>=h-m)&&Math.abs((e[0]-i.a[0])*u-(e[1]-i.a[1])*d)<=n+1e-9)return!0}return!1}(e.pointer,t.target,e.intervals,t.envelope,Math.max(1e-9,Math.min(e.baseTolerance,.04*e.gridStep)))),i=t.filter(t=>!!t.target.partitionHost&&e.renderedLength+2*t.target.physicalHalfWidth>t.length+1e-9).sort(Xg),n=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(Xg);let r=n[0];if(!r)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=n.filter(e=>Math.abs(e.distance-r.distance)<=1e-9&&Math.abs(e.perpendicular-r.perpendicular)<=1e-9),s=o.filter(e=>e.target.partitionHost);if(s.length){if(1!==new Set(s.map(e=>e.target.partitionHost.id)).size||s.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=s[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],n=Math.hypot(t,i),a=t/n,l=i/n;if(!o.every(t=>{const i=t.target.b[0]-t.target.a[0],n=t.target.b[1]-t.target.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,c=Math.abs(a*s-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*a);return c&&h<=1e-9}))return{candidate:null,jambBlockedTarget:null};r=e}if(r.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:a,length:l}=r,c=a.b[0]-a.a[0],h=a.b[1]-a.a[1],d=c/l,u=h/l,p=a.partitionHost?a.physicalHalfWidth:0,m=Math.min(Math.max(0,e.renderedLength)/2+p,l/2);let _=r.along;const g=Math.max(e.gridStep,1e-9),f=l/2;_=Math.abs(_-f)<=g/2?f:Math.round(_/g)*g,_=Math.max(m,Math.min(l-m,_));const v=a.a[0]+d*_,y=a.a[1]+u*_,b=Math.max(0,e.renderedLength)/2,w=_-b,k=_+b,x=Math.max(0,w),$=Math.max(0,l-k),S=[a.a[0]+d*(w-x/2),a.a[1]+u*(w-x/2)],M=[a.a[0]+d*(k+$/2),a.a[1]+u*(k+$/2)];let C=180*Math.atan2(h,c)/Math.PI;C>=90?C-=180:C<-90&&(C+=180);const R=Math.abs(_-f)<=1e-9;return{candidate:{presetRevision:e.preset.revision,geometryRevision:e.geometryRevision,pointer:[e.pointer[0],e.pointer[1]],type:e.preset.type,lengthCm:e.preset.lengthCm,flipH:e.preset.flipH,flipV:e.preset.flipV,x:v,y:y,angle:C,renderedLength:e.renderedLength,target:a,...a.partitionHost?{host:{...a.partitionHost,t:l>0?_/l:0}}:{},measure:{labels:[{distance:x,midpoint:S},{distance:$,midpoint:M}],guide:R?{x:v,y:y,angle:C}:null}},jambBlockedTarget:null}}function Jg(e,t,i,n,r=1e-6){return e.presetRevision===i&&e.geometryRevision===n&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=r}function Qg(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}let ef=0;const tf="1.72.0-beta.2",nf="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91",rf=1500,of=new Map;let sf=0,af=1e4;const lf="houseplan_card_layout_v1",cf="houseplan_card_cfg_v1",hf="houseplan_card_zoom_v1",df="houseplan_card_nav_v1",uf="houseplan_card_kiosk_v1",pf="houseplan_card_view_v1",mf="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",_f=1e3,gf=(e,t)=>{const i=Math.min(e.x,t.x),n=Math.min(e.y,t.y);return{x:i,y:n,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-n}},ff=(e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)},vf=e=>{history.pushState(null,"",e),ff(window,"location-changed",{replace:!1})},yf=(e,t)=>{let i,n=null;const r=(...r)=>{clearTimeout(i),n=r,i=window.setTimeout(()=>{i=void 0;const t=n;n=null,t&&e(...t)},t)};return r.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=n;n=null,t&&e(...t)},r.cancel=()=>{clearTimeout(i),i=void 0,n=null},r.pending=()=>void 0!==i,r},bf=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}},wf=["edges","corners"];class kf extends he{constructor(){super(...arguments),this._editorRuntime=null,this._onboardingRuntime=null,this._editorRuntimeLoadingVisible=!1,this._backdropGuard=null,this._editorModeRequest=0,this._warmModeRequest=0,this._editorRuntimeLoader=new mg({expectedFingerprint:nf,load:async e=>{const t=0===e?await import("./houseplan-editor-runtime-CaF5LJAh.js"):await import((()=>{const e=new URL("./houseplan-editor-runtime-CaF5LJAh.js",import.meta.url);return e.searchParams.set("hp_retry",`${tf}-${++ef}`),e.href})());return{fingerprint:t.EDITOR_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanEditorRuntime(this)}},install:e=>{this._editorRuntime=e},stateChanged:e=>this._editorRuntimeStateChanged(e),failed:(e,t)=>{console.error("[houseplan] unable to load editor runtime",e),this._showToast(ug(e=>this._t(e),t))}}),this._onboardingRuntimeLoader=new mg({expectedFingerprint:nf,load:async e=>{const t=0===e?await import("./houseplan-onboarding-runtime-BiIOObYb.js"):await import((()=>{const e=new URL("./houseplan-onboarding-runtime-BiIOObYb.js",import.meta.url);return e.searchParams.set("hp_retry",`${tf}-${++ef}`),e.href})());return{fingerprint:t.ONBOARDING_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanOnboardingRuntime(this)}},install:e=>{this._onboardingRuntime=e},failed:(e,t)=>{console.error("[houseplan] unable to load onboarding runtime",e),this._showToast(ug(e=>this._t(e),t))}}),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._cfgContentFingerprint="",this._unsubCfg=null,this._unsubLayout=null,this._unsubVirtual=null,this._liveSyncAttempt=null,this._liveSyncGeneration=0,this._liveSyncConnection=null,this._layoutRev=0,this._layoutContentFingerprint="",this._virtualLights=Kc(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._areaRelocationIds=new Set,this._areaSnapshotCleanupCandidates=new Map,this._areaRelocationSyncKey="",this._areaRelocationWrite=Promise.resolve(),this._tip=null,this._hoverRoom=null,this._pointerModality=new p_(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...rr},this._decorStyleSeeded=!1,this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._decorImagePalette=null,this._decorAssetCatalog=[],this._decorAssets=new Map,this._decorAssetBusy=!1,this._furnCategory=null,this._furnPreviewInput=null,this._furnTouchPending=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this._cancelDevicePressFeedback(),e.matches&&this._cancelCameraTransition(!0),e.matches&&this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:e.matches&&this._modeTransition.active&&this._cancelModeTransition(!0),this.requestUpdate()},this._editorChromeMode="plan",this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest=0,this._modeTransitionTargetZoom=1,this._modeTransitionEditorCamera=null,this._modeTransition=new q_({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._cameraTransitionFit=null,this._cameraTransition=new X_({frame:e=>this._applyCameraTransitionFrame(e),settled:e=>this._settleCameraTransition(e)}),this._editorSecondaryCopy={groupActive:(e,t)=>this._t("editor.group_active",{group:e,item:t}),openGroup:e=>this._t("editor.open_group",{group:e}),disabledAction:(e,t)=>this._t("editor.disabled_action",{action:e,reason:t})},this._tool="draw",this._geometryHistory=new M_(50),this._devicePositionHistory=new M_(50),this._devicePositionBusy=!1,this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._partitionDeleteDialog=null,this._roomDeleteDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCacheValue=null,this._wallUnionPool=new Map,this._isoGeometryCache=new Map,this._isoFallback=new Set,this._openingTunnelCache=null,this._openingWallIndexCache=new Map,this._openingPlacementIntervalsCache=null,this._openingDimensionContextCache=null,this._planSnapGeometryCache=null,this._planStructuralGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._physicalBodiesCache=null,this._lightPhysicalBodiesCache=null,this._cleanFloorCache=new Map,this._innerContourCache=new Map,this._glowRuntimeState=Cm(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._roomFocus=null,this._roomPointer=null,this._doubleFit=new lg,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._touchContacts=new Map,this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._connectedPath="",this._routeDepartureHandled=!1,this._onLocationChanged=()=>{this._connectedPath&&location.pathname!==this._connectedPath?this._leaveCardRoute():location.pathname===this._connectedPath&&(this._routeDepartureHandled=!1)},this._touchGestureGuard={capture:!0,handleEvent:e=>this._guardTouchGesture(e)},this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootSettling=!1,this._bootSettleRaf=0,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._dangerConfirm=null,this._dangerConfirmController=new ge(e=>{this._dangerConfirm=e}),this._confirmDanger=e=>this._config&&this.hass?"warm"===this._syncDangerConfirmLocaleGate()||this._dangerConfirmMissingSpace()?Promise.resolve(!1):this._dangerConfirmController.confirm(e):Promise.resolve(!1),this._cancelDangerConfirm=()=>{this._dangerConfirmController.cancel()},this._onDangerConfirmDecision=e=>{this._dangerConfirmController.resolve(e.detail.token,e.detail.accepted)},this._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._haSupportApi=null,this._haDecorAssetsApi=null,this._decorAssetSyncToken=0,this._alignDialog=null,this._settingsDialog=null,this._supportDialog=null,this._backupExportDialog=null,this._backupImportDialog=null,this._sunRaysCache=null,this._dayCycleTimer=0,this._dayCycleClockKey="",this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._nativeMoreInfoEntity=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++sf,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={alpha:!1,active:Object.freeze([]),space:""},this._viewPreference={},this._renderProjection="flat",this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotLayout=null,this._capturedSnapshotActivity="",this._capturedSnapshotConfigEpoch=-1,this._capturedSnapshotVirtual="",this._lastValidStageSize=null,this._pendingRefitSize=null,this._refitRaf=0,this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"===e.kind)return this._clearRoomFocus(!0),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._cancelCameraTransition(!0),void(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0));if(this._vacJumpOnce=!0,!e.long){const e=Date.now();let t=!1;for(const i of this._activityRt.values())!i.flashKind||(i.expiresAt||i.flashTs+l_)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&this.requestUpdate())}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._resumeSettling=!0,this._loading?this.requestUpdate():this._loadFromServer()},this._resumeSettling=!1,this._viewportInvalidAt=0,this._vacFit=null,this._vacAllCamerasFor=null,this._vacAllCameraCache=null,this._vacCalConfirm=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._tabDrag=null,this._tabDragRelease=null,this._tabSuppressClick=!1,this._tabOrderWarned=!1,this._onLabsSnapshot=e=>{const t=this._effectiveProjection();this._labs=e;const i=this._effectiveProjection();this._convertProjectionView(t,i),this.requestUpdate()},this._onHashChange=()=>{if(this._hasFixedFloor)return;const e=this._hashSpace();if(e&&this._model.find(t=>t.id===e)&&e!==this._space){if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._selId=null,this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate()}},this._drag=null,this._deviceDrag=null,this._rlResize=null,this._holdFired=!1,this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._loading||this._loadFromServer()},this._cfgEpoch=0,this._cfgEpochPreservedConfig=null,this._modelCache=null,this._emptySpaceStateActive=!1,this._decorSnapCache=null,this._markerPreviewMemo=null,this._markerPreviewDevicesMemo=null,this._showHidden=!1,this._connHooked=null,this._connectionWasLost=!1,this._haRegistryConnection=null,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._onHaRegistryUpdate=()=>{const e=ph(this.hass);e.revision===this._haRegistryRev&&this._devices.length||(this._haRegistryRev=e.revision,this._planHassMemo=null,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())},this._onConnReady=()=>{if(this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,uh(this.hass),this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(this._onConnReady,400));!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._loading||this._loadFromServer()},this._onConnLost=()=>{this._booting&&!this._continuity.hasCompleteFrame||(this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost())},this._signer=new Hl(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=yf(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=Qr(this._layout[t]);e&&(this._layout={...this._layout,[t]:e},this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(e=>this._noteLayoutRev(e)).catch(e=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)}))}this._cacheSnapshot()}else this._persistLocalLayout()},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>this._editorRuntimeOrThrow()._undoGeometry(),this._redoGeometry=()=>this._editorRuntimeOrThrow()._redoGeometry(),this._saveConfigDebounced=yf(()=>{this._serverCfg&&this._writeConfig().catch(e=>{const t=!0===e?.physicalGeometryRolledBack;"geometry-unsafe"!==e?.code&&("wall_model_client_outdated"===e?.code?this._showToast(this._t("toast.wall_model_client_outdated")):"conflict"===e?.code?(this._showToast(this._t("toast.conflict")),t||(this._cancelPath(),this._reloadConfigOnly(!0))):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)})),t&&this._reloadRejectedPhysicalWrite())})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>this._editorRuntimeOrThrow()._savePhysicalDialog(),this._deletePhysicalSelection=()=>this._editorRuntimeOrThrow()._deletePhysicalSelection(),this._confirmPartitionDelete=()=>this._editorRuntimeOrThrow()._confirmPartitionDelete(),this._deleteDraftWhole=()=>this._editorRuntimeOrThrow()._deleteDraftWhole(),this._deleteDraftSegment=()=>this._editorRuntimeOrThrow()._deleteDraftSegment(),this._confirmRoomDelete=e=>this._editorRuntimeOrThrow()._confirmRoomDelete(e),this._rebindPartitionOpening=()=>this._editorRuntimeOrThrow()._rebindPartitionOpening(),this._keepClosedAsPartitions=()=>this._editorRuntimeOrThrow()._keepClosedAsPartitions(),this._toggleServerPlans=async()=>this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._toggleServerPlans():this._editorRuntimeOrThrow()._toggleServerPlans(),this._aspectJob=null,this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=qn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(this._editorRuntime)return this._editorRuntime._openSettingsDialog();this._ensureEditorRuntime().then(e=>{e&&this._openSettingsDialog()})},this._openSupportDialog=()=>{this._editorRuntime?this._editorRuntime._openSupportDialog():this._ensureEditorRuntime().then(e=>{e&&this._openSupportDialog()})},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._editorRuntimeOrThrow()._openAlignDialog(),this._toggleOptimizeLivePositions=()=>this._editorRuntimeOrThrow()._toggleOptimizeLivePositions(),this._optimizeUndoBusy=!1,this._openBackupExport=()=>this._editorRuntimeOrThrow()._openBackupExport(),this._openRulesDialog=()=>this._editorRuntimeOrThrow()._openRulesDialog(),this._climateCache=null,this._gearPtCache=new WeakMap}_editorRuntimeOrThrow(){if(!this._editorRuntime)throw new Error("Houseplan editor runtime is not loaded");return this._editorRuntime}async _ensureEditorRuntime(){return this._editorRuntimeLoader.ensure()}async _ensureOnboardingRuntime(){return this._onboardingRuntimeLoader.ensure()}_editorRuntimeStateChanged(e){clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,"loading"!==e?(this._editorRuntimeLoadingVisible=!1,this.requestUpdate()):this._editorRuntimeLoadingTimer=window.setTimeout(()=>{this._editorRuntimeLoadingVisible=!0,this.requestUpdate()},150)}async _requestMode(e,t=!0,i=!1){const n=++this._editorModeRequest;if(i&&(this._warmModeRequest=n,this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._pendingRefitSize=null),"view"===e||await this._ensureEditorRuntime()){if(n===this._editorModeRequest&&this.isConnected)return i?(this._adoptMode(e),this._warmRevivePending&&(clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmReviveDialog()),this.requestUpdate(),void this.updateComplete.then(()=>requestAnimationFrame(()=>requestAnimationFrame(()=>{if(this._warmModeRequest!==n||n!==this._editorModeRequest)return;const e=this._stageEl;this._lastValidStageSize=e&&e.clientWidth>0&&e.clientHeight>0?[e.clientWidth,e.clientHeight]:null,this._pendingRefitSize=null,this._warmModeRequest=0})))):void this._setMode(e,t);this._warmModeRequest===n&&(this._warmModeRequest=0)}else this._warmModeRequest===n&&(this._warmModeRequest=0)}_seedDecorStyle(e){if(this._decorStyleSeeded||!e)return;this._decorStyleSeeded=!0;const t=e.settings?.decor_default_style;t&&(this._decorStyle=function(e,t){if(!e||"object"!=typeof e)return{...t};const i=e,n="string"==typeof i.color&&i.color?i.color:t.color,r="string"==typeof i.fill_color&&i.fill_color?i.fill_color:t.fillColor,o=Number(i.width_cm);return{color:n,opacity:or(i.opacity,t.opacity),widthCm:Number.isFinite(o)&&o>0?Math.max(.1,Math.min(100,o)):t.widthCm,fill:"boolean"==typeof i.fill?i.fill:t.fill,fillColor:r,fillOpacity:or(i.fill_opacity,t.fillOpacity)}}(t,rr))}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}get _modeTransitionBusy(){return this._modeTransitionPreparing||this._modeTransition.active}get _doubleFitEnabled(){return!("view"!==this._mode||this._vacFit||this._modeTransitionBusy||this._touchSequenceMultitouch||"steady"!==this._continuity.state||this._continuity.overlayBlocksInteraction)}_cameraState(){const e=this._viewOr(this._baseVb());return{zoom:this._zoom,viewBox:{...e}}}_normalizeCameraState(e){const t=this._cameraTransitionFit||vi(this._baseVb(),this._stageAspect());return{zoom:Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,e.zoom)),viewBox:this._clampView({...e.viewBox},t)}}_applyCameraTransitionFrame(e){const t=this._normalizeCameraState(e.presented);e.presented=t,this._zoom=t.zoom,this._view={...t.viewBox},this.requestUpdate()}_settleCameraTransition(e){const t=this._normalizeCameraState(e.to);this._zoom=t.zoom,this._view={...t.viewBox},this._cameraTransitionFit=null,"room"!==e.reason&&this._saveZoom(),this.requestUpdate()}_cancelCameraTransition(e=!1,t=!1){const i=this._cameraTransition.state?.reason,n=t&&this._cameraTransition.active?this._cameraTransition.presented?.zoom:void 0;this._cameraTransition.cancel(e),this._cameraTransitionFit=null,void 0!==n&&"room"!==i&&this._saveZoom()}_prepareCameraCommand(){this._modeTransitionBusy&&this._cancelModeTransition(!0),"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1))}_startCameraTransition(e,t,i,n){const r=this._cameraState(),o=this._cameraTransition.target;return(!o||!G_(o,e))&&(G_(r,e)?(this._cancelCameraTransition(!1),!1):(this._cameraTransitionFit={...t},this._cameraTransition.start(r,e,i,this._reducedMotion?0:n),!0))}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const n=document.createElement("span");n.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(n);const r=getComputedStyle(n).color||t;return n.remove(),r}_currentModeVisual(e=this._mode){const t=this._modeTransition.presented||this._modeTransitionVisual;if(t)return{...t,viewport:{...t.viewport,viewBox:{...t.viewport.viewBox}}};const i=this._stageEl;if(!i||i.clientWidth<=0||i.clientHeight<=0)return null;const n=this.renderRoot.querySelector(".editorchrome"),r=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),s=this.renderRoot.querySelector(".hp-backdrop"),a=this.renderRoot.querySelector(".zoomwrap"),l=a?getComputedStyle(a).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:n?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:N_(r,i.clientWidth),stageColor:getComputedStyle(i).backgroundColor||"rgb(255, 255, 255)",paperColor:o?getComputedStyle(o).fill:"rgb(255, 255, 255)",sceneBrightness:Number.isFinite(c)?c:1,architectureOpacity:"decor"===e?.35:1,backdropOpacity:s&&Number(getComputedStyle(s).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,n,r){const o=vi(this._baseVb(),n/Math.max(1,r)),s=Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,e)),a=o.w/s,l=o.h/s,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-a/2,y:h-l/2,w:a,h:l},o)}_targetStageColor(e){return"view"!==e?"rgb(255, 255, 255)":this._cssColor(this._stageBg(this._spaceDisplayForRender()),this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"))}_targetPaperColor(e){return"view"===e&&this._spaceModel()?.bg?this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"):"rgb(255, 255, 255)"}_targetBrightness(e){return 1}_applyModeTransitionFrame(e){e.targetMode===this._mode&&(this._modeTransitionVisual=e.presented,this._view={...e.presented.viewport.viewBox},this.requestUpdate())}_settleModeTransition(e){if(e.targetMode!==this._mode)return;const t=this._modeTransitionRequest;this._view={...e.to.viewport.viewBox},this._zoom=this._modeTransitionTargetZoom,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._lastValidStageSize=[e.to.stageWidth,e.to.stageHeight],"view"===this._mode&&(this._saveZoom(),this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate(),this.updateComplete.then(()=>{if(!this.isConnected||t!==this._modeTransitionRequest||this._modeTransitionBusy||e.targetMode!==this._mode)return;const i=this._stageEl;if(i&&i.clientWidth>0&&i.clientHeight>0&&(Math.abs(i.clientWidth-e.to.stageWidth)>.5||Math.abs(i.clientHeight-e.to.stageHeight)>.5)){const e=this._view;this._lastValidStageSize=[i.clientWidth,i.clientHeight],this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}const n=this.renderRoot.activeElement;(!n||!n.isConnected||!!n.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){this._cancelCameraTransition(!1);const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){e!==this._mode&&this._clearRoomFocus(!0),this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,n){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const r=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,n,r,o),this._lastValidStageSize=[r,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,n,r,o){this.updateComplete.then(()=>{if(!this.isConnected||e!==this._modeTransitionRequest||this._mode!==i)return void(e===this._modeTransitionRequest&&(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this.requestUpdate()));const s=this.renderRoot.querySelector(".editorchrome"),a=s?.querySelector(".editorchrome-inner"),l="view"===i?0:a?.scrollHeight||a?.getBoundingClientRect().height||0,c=Math.max(1,t.stageHeight+t.editorChromeHeight),h=Math.max(1,c-l),d=this._stageEl?.clientWidth||t.stageWidth;if(d<=0||h<=0)return this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._applyView(n,r,o),void this.requestUpdate();const u=this._viewForModeTarget(n,r,o,d,h),p={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:N_(u,d),stageColor:this._targetStageColor(i),paperColor:this._targetPaperColor(i),sceneBrightness:this._targetBrightness(i),architectureOpacity:"decor"===i?.35:1,backdropOpacity:"decor"===i&&"backdrop"!==this._decorTool?.5:1,viewWeight:"view"===i?1:0,editorWeight:"view"===i?0:1,toolbarContentOpacity:"view"===i?0:1};this._modeTransitionPreparing=!1;const m=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,p,i,this._reducedMotion||m?0:220)})}get _hasFixedFloor(){return!!this._config&&Object.prototype.hasOwnProperty.call(this._config,"floor")}_fixedFloorState(e=this._model,t=this._loadOk){const i=this._config?.floor,n=function(e){if(!e.hasFloor)return{kind:"absent"};const t=e.floor;if("string"==typeof t)return t.length?e.spaceIds.includes(t)?{kind:"valid",id:t,source:"id"}:{kind:"invalid",reason:"unknown-id",value:t}:{kind:"invalid",reason:"empty-id",value:t};if("number"==typeof t){if(!Number.isFinite(t))return{kind:"invalid",reason:"non-finite-index",value:t};if(!Number.isInteger(t))return{kind:"invalid",reason:"fractional-index",value:t};if(t<0)return{kind:"invalid",reason:"negative-index",value:t};const i=e.spaceIds[t];return void 0===i?{kind:"invalid",reason:"out-of-range-index",value:t}:{kind:"valid",id:i,source:"index"}}return{kind:"invalid",reason:"invalid-type",value:t}}({spaceIds:e.map(e=>e.id),hasFloor:this._hasFixedFloor,floor:i});return!this._hasFixedFloor||t?n:"number"==typeof i?"valid"===n.kind||"invalid"===n.kind&&"out-of-range-index"===n.reason?{kind:"pending",value:i}:n:"valid"===n.kind||"invalid"===n.kind&&"unknown-id"!==n.reason?n:{kind:"pending",value:i}}_canCommitSpace(e,t=!1){if(t||!this._hasFixedFloor)return!0;const i=this._fixedFloorState();return"valid"===i.kind&&i.id===e}_commitSpace(e,t=!1){return!!this._canCommitSpace(e,t)&&(e!==this._space&&(this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._cancelCameraTransition(!1),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._editorRuntime?._clearFurniturePreview()),this._space=e,!0)}_slideTo(e,t){if(e===this._space)return!0;if(!this._canCommitSpace(e))return!1;if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return!1;this._cancelModeTransition(!0);const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;return this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._editorSecondary?.closeForNavigation(),this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=t,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate()),!0}get _canReorderTabs(){return Qg({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){Qg({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(bf(e),this._tabDragRelease=e=>this._tabPointerUp(e),window.addEventListener("pointerup",this._tabDragRelease),window.addEventListener("pointercancel",this._tabDragRelease),this._tabDrag={id:t,pointerId:e.pointerId,x:e.clientX,y:e.clientY,moved:!1,targetId:null,placement:null})}_tabDropTargetAt(e,t,i){const n=this._model.map(e=>e.id),r=n.indexOf(i);if(r<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const s of o){const o=s.dataset.id||"";if(!o||o===i)continue;const a=s.getBoundingClientRect();if(ea.right||ta.bottom)continue;const l=n.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,n;const r=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===r?.targetId&&t.placement===r?.placement||(this._tabDrag={...t,moved:!0,targetId:r?.targetId||null,placement:r?.placement||null})}_tabPointerUp(e){const t=this._tabDrag;if(t&&t.pointerId!==e.pointerId)return;const i="pointerup"===e.type&&t?.moved?this._tabDropTargetAt(e.clientX,e.clientY,t.id):null;"pointerup"===e.type&&t?.moved&&this._suppressNextTabClick(),this._endTabDrag(),t?.moved&&i&&this._commitTabOrder(t.id,i.targetId)}_endTabDrag(){this._tabDrag=null,this._tabDragRelease&&(window.removeEventListener("pointerup",this._tabDragRelease),window.removeEventListener("pointercancel",this._tabDragRelease),this._tabDragRelease=null)}_tabClick(e){if(this._tabSuppressClick)return this._tabSuppressClick=!1,clearTimeout(this._tabSuppressClickTimer),void(this._tabSuppressClickTimer=void 0);this._pickSpace(e)}_suppressNextTabClick(){this._tabSuppressClick=!0,clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=window.setTimeout(()=>{this._tabSuppressClick=!1,this._tabSuppressClickTimer=void 0},0)}_commitTabOrder(e,t){const i=this._serverCfg;if(!i||!this._canReorderTabs)return;const n=this._model.map(e=>e.id),r=function(e,t,i){const n=e.indexOf(t),r=e.indexOf(i);if(n<0||r<0||n===r)return e;const o=e.slice();return o.splice(n,1),o.splice(r,0,t),o}(n,e,t);if(r===n)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),s=function(e,t,i,n=()=>""){if(!i)return[];const r=[];for(const o of e){if(!o||!0===o.removed)continue;const e="string"==typeof o.id?o.id:"";if(!e)continue;if("string"==typeof o.space&&o.space)continue;const s=("string"==typeof o.area?o.area:"")||n(e)||"";s&&t[s]||r.push({id:e,space:i})}return r}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),n[0]||"",e=>o.get(e)||"");if(s.length){const e=new Map(s.map(e=>[e.id,e.space]));for(const t of i.markers||[]){const i=e.get(String(t.id));i&&(t.space=i)}}i.spaces=function(e,t){const i=new Map(t.map((e,t)=>[e,t]));return e.map((e,t)=>({space:e,index:t})).sort((e,t)=>(i.get(String(e.space?.id))??Number.MAX_SAFE_INTEGER)-(i.get(String(t.space?.id))??Number.MAX_SAFE_INTEGER)||e.index-t.index).map(e=>e.space)}(i.spaces||[],r),this._saveConfig(),this._tabOrderWarned||(this._tabOrderWarned=!0,this._showToast(this._t("toast.space_order_changed")))}_pickSpace(e){if(this._endTabDrag(),e===this._space)return;const t=this._model.map(e=>e.id),i=t.indexOf(this._space),n=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&n0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const e=this._model.map(e=>e.id),t=e.indexOf(this._space);this._slideTo(e[(t+1)%e.length],"left"),this._showKioskDots()}}_syncCycleTimer(){clearInterval(this._cycleTimer),this._cycleTimer=void 0,this.isConnected&&!this._hasFixedFloor&&this._config?.kiosk&&Number(this._config.cycle)>0&&(this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle)))}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}get _wallUnionCache(){return this._wallUnionCacheValue}set _wallUnionCache(e){this._wallUnionCacheValue=e,null===e&&this._wallUnionPool.clear()}get _glowClipCache(){return this._glowRuntimeState.clipCache}get _glowGeometryWarnings(){return this._glowRuntimeState.geometryWarnings}get _glowFeatherUnits(){return this._glowRuntimeState.featherUnits}set _glowFeatherUnits(e){this._glowRuntimeState.featherUnits=e}get _glowRenderedSources(){return this._glowRuntimeState.renderedSources}get _glowLastAppearance(){return this._glowRuntimeState.lastAppearance}get _glowFeatherSuspendUntil(){return this._glowRuntimeState.featherSuspendUntil}set _glowFeatherSuspendUntil(e){this._glowRuntimeState.featherSuspendUntil=e}get _glowFeatherResumeTimer(){return this._glowRuntimeState.featherResumeTimer}set _glowFeatherResumeTimer(e){this._glowRuntimeState.featherResumeTimer=e}get _glowSourceSeq(){return this._glowRuntimeState.sourceSeq}set _glowSourceSeq(e){this._glowRuntimeState.sourceSeq=e}_syncDangerConfirmLocaleGate(){return this._config&&this.hass?hu(this,_u,wu(this.hass,this._config.language)):"ready"}_dangerConfirmMissingSpace(){const e=this._model;if(!e.length)return!1;const t=this._fixedFloorState(e);return"pending"!==t.kind&&"invalid"!==t.kind&&!this._spaceModel()}_newContinuityController(){return new Ol(()=>{this._resumeSettling="steady"!==this._continuity.state,this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}get _labsIso(){return this._labs.active.includes("iso")}get _desiredProjection(){return"view"===this._mode&&this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat"}_saveViewPreference(){try{localStorage.setItem(pf,JSON.stringify(this._viewPreference))}catch{}}_logicalViewCenter(e){const t=this._view;if(!t)return null;const i=[t.x+t.w/2,t.y+t.h/2],n="iso"===e?tg(i):i;return{x:n[0],y:n[1]}}_convertProjectionView(e,t){if(e===t)return;this._clearRoomFocus(!0);const i=this._logicalViewCenter(e);this._view=null;const n=i?"iso"===t?eg([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,n?.[0],n?.[1]),this._warmPatch({vp:this._warmViewportState()}),this.requestUpdate()}_setProjection(e){if(!this._labsIso||"view"!==this._mode)return;const t=this._effectiveProjection();if(this._viewPreference={...this._viewPreference,[this._space]:e},"iso"===e){const e=this._isoSceneKey();e&&this._isoFallback.delete(e)}this._saveViewPreference();const i=this._effectiveProjection();this._convertProjectionView(t,i)}_hashSpace(){return xg(window.location.hash||"")}connectedCallback(){this._connectedPath=location.pathname,this._routeDepartureHandled=!1,window.addEventListener("location-changed",this._onLocationChanged),window.addEventListener("popstate",this._onLocationChanged),this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1);const e=v_(this.ownerDocument);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=Pl(this.ownerDocument,this._pageVisibility),this._languageFailureUnsub?.(),this._languageFailureUnsub=(t=()=>{this._showToast(this._t("toast.locale_load_failed"))},mu.add(t),()=>mu.delete(t)),super.connectedCallback(),this._pointerModality.connect(this.ownerDocument.defaultView);const i=this.ownerDocument.defaultView?.MutationObserver;i&&(this._pointerHoverObserver=new i(e=>{for(const t of e)for(const e of t.addedNodes)this._syncPointerHoverSubtree(e)}),this._pointerHoverObserver.observe(this.renderRoot,{childList:!0,subtree:!0})),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),f_(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())}),this.hass&&this._ensureHaRegistryAuthority(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>this._referencedContentUrls()),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e){Rg.add(e);const t=zg();return"undefined"==typeof window&&e(t),()=>{Rg.delete(e)}}(this._onLabsSnapshot),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},rf)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._loadOk&&this._ensureLiveSyncSubscriptions(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0)),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._connectedPath&&location.pathname!==this._connectedPath&&this._leaveCardRoute(),window.removeEventListener("location-changed",this._onLocationChanged),window.removeEventListener("popstate",this._onLocationChanged),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._languageFailureUnsub?.(),this._languageFailureUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._warmModeRequest=0,this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._bootSettleRaf&&(cancelAnimationFrame(this._bootSettleRaf),this._bootSettleRaf=0),this._bootSettling=!1;for(const e of this._activityRt.values())clearTimeout(e.timer);window.removeEventListener("keydown",this._keyHandler),this._endTabDrag(),clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=void 0,this._tabSuppressClick=!1,clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null,this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,this._editorRuntimeLoadingVisible=!1,this._modeTransition.dispose(),this._cameraTransition.dispose(),this._cameraTransitionFit=null,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest++,this._slideTimer=void 0,this._slide="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),this._cancelDeviceDrag(),window.removeEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=void 0,clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),this._unsubVirtual&&(this._unsubVirtual(),this._unsubVirtual=null),this._unsubTrail&&(this._unsubTrail(),this._unsubTrail=void 0),this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=null,clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),Lm(this._glowRuntimeState,this._glowRuntimeHost),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._touchContacts.clear(),this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._pointerHoverObserver?.disconnect(),this._pointerHoverObserver=void 0,this._pointerModality.disconnect(),this._editorSecondary?.reset(),this._resumeSettling=!1,this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_onKey(e){if("Escape"===e.key&&this._vacFit){if(this._vacFit.busy)return;return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void e.stopPropagation()}if("Escape"===e.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._vacCalConfirm)return void(this._vacCalConfirm.busy||(this._vacCalConfirm=null));if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void this._closeInfoCard();if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return this._alignDialog=null,void(this._preflightClipboardFallback=null);if(this._backupImportDialog)return void(this._backupImportDialog=null);if(this._backupExportDialog)return void(this._backupExportDialog=null);if(this._supportDialog)return void this._editorRuntime?._closeSupportDialog();if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void this._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary?.hasOpenGroup)return e.preventDefault(),void this._editorSecondary?.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),n=t.some(e=>e?.classList?.contains?.("editor-secondary")),r=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),s=/^[a-z]$/.test(o),a="z"===o||!s&&"KeyZ"===e.code,l="y"===o||!s&&"KeyY"===e.code,c=r&&(a&&e.shiftKey||l),h=r&&a&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry();if(("Delete"===e.key||"Backspace"===e.key)&&this._decorSel&&!i&&!n)return e.preventDefault(),void this._decorDeleteSel();const t="ArrowLeft"===e.key?[-this._gridPitch,0]:"ArrowRight"===e.key?[this._gridPitch,0]:"ArrowUp"===e.key?[0,-this._gridPitch]:"ArrowDown"===e.key?[0,this._gridPitch]:null;return!t||r||e.altKey||"select"!==this._decorTool||!this._decorSel||!this._decorList.some(e=>e.id===this._decorSel)||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag||i||n||this._editorSecondaryDialogBlocked?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._furnPalette=null,this._furnCategory=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorNudge(t[0],t[1]))}if("devices"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoDevicePosition()):h?(e.preventDefault(),void this._undoDevicePosition()):void("Escape"===e.key&&this._deviceDrag&&(e.preventDefault(),this._cancelDeviceDrag()))}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!n)return e.preventDefault(),void this._deletePhysicalSelection();if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():this._wallFaceBatch?(this._roomDialogCancel(),void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint())):"draw"===this._tool&&this._path.length?void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint()):"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry();if("Escape"===e.key)return this._physicalDrag||this._physicalRotate?(e.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(e.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length?(e.preventDefault(),void this._finishWallChain()):this._physicalSel?(e.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():("exit-tool"===this._resize?.escapeIdle()&&(this._tool="draw"),void this.requestUpdate())):"split"===this._tool?(e.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(e.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(e.preventDefault(),void(this._wallDialog=null)):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"column"!==this._tool||(e.preventDefault(),"opening"===this._tool&&this._clearOpeningPlacement(!0),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const e=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const t=this._curSpaceCfg,i=(t.room_drafts||[]).findIndex(e=>e.id===this._activeDraftId);if(i>=0){const n=Array.isArray(t.room_drafts[i]?.segments)?t.room_drafts[i].segments:[];this._path.length<2?(t.room_drafts.splice(i,1),t.room_drafts.length||delete t.room_drafts,this._activeDraftId=null):t.room_drafts[i]={id:this._activeDraftId,points:this._path.map(e=>[e[0]/_f,e[1]/_f]),segments:this._draftSegmentCms.map((e,t)=>({...n[t]||{},cm:e}))},this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),e)}return}this._path=this._path.slice(0,-1)}}_undoActiveDraftPoint(){const e=this._activeDraftId,t=this._path[0]?[...this._path[0]]:null,i=this._geometryHistory.undo();if(!i)return void this._undoPoint();if(!this._applyGeometryState(i.before,!0))return void this._geometryHistory.clear();const n=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;n?(this._activeDraftId=n.id,this._path=n.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,n.segments.map(e=>e.cm),n.id),this._resumeDraftBySpace[this._space]=n.id):(this._activeDraftId=null,this._path=t?[t]:[],this._draftSegmentCms=[],e&&this._resumeDraftBySpace[this._space]===e&&delete this._resumeDraftBySpace[this._space]),this._clearPlanSnapHover(),this._showToast(this._t("history.undone",{name:i.name}))}static async getConfigElement(){return await import("./editor-CrMXGx3s.js"),document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of of.values())for(const t of e)clearTimeout(t.evict);of.clear(),af=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const n of of.values())for(const r of n)e++,r.dlg&&(t++,i.push(r.dlg.kind));return{keys:of.size,slots:e,dlgs:t,drafts:i}}setConfig(e){const t=this._config,i=!!t&&Object.prototype.hasOwnProperty.call(t,"floor");this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...e};(i!==this._hasFixedFloor||t?.floor!==this._config.floor)&&(this._hashApplied=!1,this._navApplied=!1,this._warmVpArmed=!1),this._config.kiosk&&(this._booting=!1,this._bootFading=!1),!this._hasFixedFloor&&e.default_floor&&this._commitSpace(e.default_floor,!0);try{this._zoomBySpace=JSON.parse(localStorage.getItem(hf)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(pf)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=zg();try{const e=JSON.parse(localStorage.getItem(uf)||"null");this._kioskScale={icon:kn(e?.icon),font:kn(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(cf)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._seedDecorStyle(this._serverCfg),this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||Il(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Il(this._layout),this._virtualLights=Kc(e.virtual_lights,this._cfgRev),this._serverStorage=!0)}catch{}this._adoptInitialSpace(this._model,this._loadOk),"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._syncCycleTimer(),this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const e=vg(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),n=of.get(e);if(!n||!n.length)return;const r=n.find(e=>e.owner===this._warmGen);if(r)return this._warmLongReturn=!!r.freed&&Date.now()-r.freed>=Dl,clearTimeout(r.evict),r.evict=0,r.freed=0,r.live=!0,this._warmSlot=r,this._warmKey=e,r.frameFingerprint&&this._continuity.adoptCompleteFrame(r.frameFingerprint),void(!this._devices.length&&r.devices?.length&&(this._devices=[...r.devices]));const{slot:o,sure:s}=((e,t,i,n)=>{const r=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===n?4:e.live?0:t?3:2};let o=null,s=0,a=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=r(i);e<=0||(e>s?(o=i,s=e,a=1):e===s&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(n,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Dl,this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},rf)),this._warmKey=e,s?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=t?new WeakRef(t):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,o.frameFingerprint&&this._continuity.adoptCompleteFrame(o.frameFingerprint),!this._devices.length&&o.devices?.length&&(this._devices=[...o.devices]),this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,path:location.pathname,place:t?new WeakRef(t):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.stageH,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0},n.push(this._warmSlot),this._warmTrim(n)))}_warmIdx(e){const t=e?.children;if(!t)return-1;for(let e=0;e4;){const t=e.findIndex(e=>!e.live);if(t<0)break;clearTimeout(e[t].evict),e.splice(t,1)}}_warmScheduleEvict(e,t){if(clearTimeout(e.evict),!(e.dlg||e.vp&&"view"!==e.vp.mode))return;const i=e.freed,n=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==n)return;var r;e.dlg=null,e.vp=(r=e.vp)&&"view"!==r.mode?{...r,mode:"view",zoom:r.snap?.space===r.space?r.snap.zoom:r.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:r,e.frameFingerprint="";const o=of.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},af+250)}_warmAdoptViewport(e){const t=this._warmVp;if(!t||this._warmSlot?.path!==location.pathname)return void(this._warmVp=null);const i=this._fixedFloorState();if(this._hashApplied||!this._model.find(e=>e.id===t.space)||"valid"===i.kind&&i.id!==t.space||this._hasFixedFloor&&"valid"!==i.kind)return void(this._warmVp=null);this._commitSpace(t.space,!0),this._navApplied=!0;const n="view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view";this._adoptMode("view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const r=this._effectiveProjection(),o=r===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=o&&t.view?{...t.view}:null,this._viewModeSnap=o&&t.snap?{...t.snap}:null,!o&&t.logicalCenter){const e="iso"===r?eg([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var s;this._tool="opening"===(s=function(e){return"partition"===e?"draw":e}(s=t.tool))?"draw":"string"==typeof s&&fg.has(s)?s:"draw",this._decorTool=t.decorTool,this._showHidden=t.showHidden,this._showFar!==t.showFar&&(this._showFar=t.showFar,this._frame=null),this._selId=t.selId,this._resize?.restoreSelection(t.rszSel),this._decorSel=t.decorSel,this._warmVpArmed=!0,"view"!==n&&this._requestMode(n,!1,!0)}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=vg(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!t)return;const e=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,path:location.pathname,place:e?new WeakRef(e):null,idx:this._warmIdx(e),live:!0,hdrH:this._hdrH,stageH:0,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0};const n=of.get(i)||[];for(n.push(this._warmSlot),of.set(i,n),this._warmTrim(n);of.size>8;){const e=of.keys().next().value;if(void 0===e||e===i)break;for(const t of of.get(e)||[])clearTimeout(t.evict);of.delete(e)}}Object.assign(this._warmSlot,e)}}_warmViewportState(){const e=this._effectiveProjection();return{space:this._space,mode:this._mode,projection:e,activeLabsIso:this._labsIso,logicalCenter:this._logicalViewCenter(e),zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._resize?.selectedRoomId,decorSel:this._decorSel}}_warmDialogState(){const e=(e,t)=>({kind:e,space:this._space,mode:this._mode,data:t});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog||this._backupExportDialog||this._backupImportDialog?null:this._openingInfo?e("openingInfo",this._openingInfo.id):this._infoCard?e("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:e("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:e("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:e("marker",this._markerDialog):this._openingDialog?e("opening",this._openingDialog):this._backdropDialog?e("backdrop",this._backdropDialog):this._decorShapeDialog?e("decorShape",this._decorShapeDialog):this._decorTextDialog?e("decorText",this._decorTextDialog):this._roomDialog?e("room",{editId:this._roomEditId,fill:this._roomFill,customFill:this._roomCustomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,wallFaceBatch:this._wallFaceBatch,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:e("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._config?.kiosk||"steady"!==this._continuity.state)return;const e={vp:this._warmViewportState(),frameFingerprint:this._continuity.frameFingerprint,devices:this._devices};if(this._warmRevivePending||(e.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const t=this.parentNode;e.place=t?new WeakRef(t):null,e.idx=this._warmIdx(t)}this._warmPatch(e)}_warmReviveDialog(){const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return void(this._warmRevivePending=!1);const t=e.dlg,i=e.freed;if(t.mode===this._mode||"view"===t.mode||this._warmVp?.mode!==t.mode||this._editorRuntime){if(this._warmRevivePending=!1,e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>af)&&t.space===this._space&&t.mode===this._mode){switch(t.kind){case"space":this._spaceDialog={...t.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...t.data,busy:!1};break;case"settings":this._settingsDialog={...t.data,busy:!1};break;case"rules":this._rulesDialog={...t.data,busy:!1};break;case"opening":this._openingDialog={...t.data};break;case"backdrop":this._backdropDialog={...t.data};break;case"decorShape":this._decorShapeDialog={...t.data};break;case"decorText":{this._decorTextDialog={...t.data};const e=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:e,end:e};break}case"room":{const e=t.data;this._roomEditId=e.editId,this._roomFill=e.fill,this._roomCustomFill=e.customFill||null,this._roomTempSrc=e.tempSrc,this._roomHumSrc=e.humSrc,this._roomSrcOpen=e.srcOpen,this._roomSrcFilter=e.srcFilter,this._roomNameScale=e.nameScale,this._roomLabelScale=e.labelScale,this._areaSel=e.areaSel,this._nameSel=e.nameSel,this._pendingSplit=e.pendingSplit,this._wallFaceBatch=e.wallFaceBatch||null,this._path=e.path,this._wallFaceBatch&&(this._activeDraftId=this._wallFaceBatch.activeDraftId,this._draftSegmentCms=[...this._wallFaceBatch.activeCms]),this._roomDialog=!0;break}case"info":{const e=this._devices.find(e=>e.id===t.data);e&&(this._infoCard=e);break}case"openingInfo":{const e=(this._curSpaceCfg?.openings||[]).find(e=>e.id===t.data);e&&(this._openingInfo=e);break}}this.requestUpdate()}}else this._warmRevivePending=!0}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=Il(this._serverCfg),this._layoutContentFingerprint=Il(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const n=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(Jc).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>n.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(cf,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,config_fingerprint:this._cfgContentFingerprint,layout:this._layout,layout_rev:this._layoutRev,layout_fingerprint:this._layoutContentFingerprint,virtual_lights:Xc(this._virtualLights)}))}catch{}}_beginContinuityCandidate(e,t,i="plan"){return this._booting&&!this._continuity.hasCompleteFrame?this._continuity.token:(this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._resumeSettling=!0,this._continuity.beginCandidate(e,i))}_continuityStageValid(){const e=this._stageEl;return!!e&&e.clientWidth>0&&e.clientHeight>0}_continuityAssetsReady(){if(!this._model.length)return!1;const e=this._model.length?this._spaceModel():null;return!e?.bg?.href||this._signer.isReady(this.hass,e.bg.href)}_initialSpaceSelection(e,t=this._loadOk){const i=this._fixedFloorState(e,t);return"valid"===i.kind?{id:i.id,source:"fixed"}:this._hasFixedFloor?{id:null,source:"none"}:function(e){const t=new Set(e.spaceIds.filter(e=>!!e));if(!t.size)return{id:null,source:"none"};const i=[["hash",!1===e.acceptHash?null:e.hashSpace],["current",e.preserveCurrent?e.currentSpace:null],["saved",e.savedSpace],["default",e.defaultSpace],["first",e.spaceIds[0]]];for(const[e,n]of i)if(n&&t.has(n))return{id:n,source:e};return{id:null,source:"none"}}({spaceIds:e.map(e=>e.id),hashSpace:this._hashSpace(),acceptHash:!this._hashApplied,currentSpace:this._space,preserveCurrent:this._hashApplied||this._navApplied||this._warmVpArmed,savedSpace:this._savedNav()?.space,defaultSpace:this._config?.default_floor})}_adoptInitialSpace(e,t=this._loadOk){const i=this._initialSpaceSelection(e,t);return i.id?(this._commitSpace(i.id,!0),"hash"===i.source&&(this._hashApplied=!0),"saved"===i.source&&(this._navApplied=!0),i):i}_candidateBackdrop(e,t=this._space){const i=Ta(e),n=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==n.kind)return"";const r=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===r)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return El([this._cfgRev,this._cfgContentFingerprint||Il(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Il(this._layout),this._space,this._mode,this._view,t,this._glowScreenBlend?"screen":"normal",this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_settleContinuityFrame(){if(this._booting||!this._continuityStageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state)return void(this._continuityAssetsReady()&&(this._renderSnapshotAt=Date.now(),this._continuity.markCompleteFrame(this._visualFrameFingerprint())));if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._continuityStageValid(),assetsReady:()=>this._continuityAssetsReady(),frameFingerprint:()=>this._visualFrameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._resumeSettling=!1,this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._warmSnapshot()):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}_onBackdropLoaded(e,t){this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return V;const e="connection"===this._continuity.recoveryReason;return W`
    {}).then(t)}function M_(e,t,i,n,r){return{previous:JSON.parse(JSON.stringify(e)),previousFingerprint:i,revision:n,attempted:t,attemptedFingerprint:r(t)}}function C_(e,t,i){const n=e._serverCfg;return!(!n||e._cfgRev!==t.revision||i(n)!==t.attemptedFingerprint)&&(e._serverCfg=t.previous,e._cfgContentFingerprint=t.previousFingerprint,e.requestUpdate(),!0)}class R_{constructor(e=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(e))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(e){this._undo.push(e),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const e=this._undo.pop()??null;return e&&this._redo.push(e),e}redo(){const e=this._redo.pop()??null;return e&&this._undo.push(e),e}removeWhere(e){const t=this._undo.length,i=this._redo.length;return this._undo=this._undo.filter(t=>!e(t)),this._redo=this._redo.filter(t=>!e(t)),t+i-this._undo.length-this._redo.length}clear(){this._undo=[],this._redo=[]}}function T_(e,t){const i=e[t];return i&&Number.isFinite(i.x)&&Number.isFinite(i.y)?{x:i.x,y:i.y,..."string"==typeof i.s?{s:i.s}:{}}:null}function D_(e,t,i){if(null===i){if(!(t in e))return e;const i={...e};return delete i[t],i}const n={...e[t]||{}};delete n.x,delete n.y,delete n.s;const r={...n,x:i.x,y:i.y,...void 0!==i.s?{s:i.s}:{}};return{...e,[t]:r}}class z_{constructor(){this.seen=null,this.diagnosticsCache=null}observe(e,t,i,n){this.invalidateDiagnosticsForHass(e,t,i),this.intake(t,n)}intake(e,t){e&&this.seen!==e&&(this.seen=e,t())}invalidate(){this.diagnosticsCache=null}invalidateDiagnosticsForHass(e,t,i){this.diagnosticsCache&&(e?.entities!==t?.entities||e?.devices!==t?.devices||[...i?.entityIds||[]].some(i=>!!e?.states?.[i]!=!!t?.states?.[i]))&&this.invalidate()}diagnostics(e,t,i){if(this.diagnosticsCache)return this.diagnosticsCache;const n={active:0,ha_disabled:0,orphaned:0,unverified:0};for(const e of t)e.removed||"virtual"===e.binding||n[i(e.binding).kind]++;return this.diagnosticsCache={registry:xh(e),bindings:n},this.diagnosticsCache}}function A_(e){const t=e;t._hassSequence++,t._renderSnapshotAt=Date.now(),t._continuity.note("hass-snapshot"),t._ensureHaRegistryAuthority(),t._planHassMemo=null,t._hookConnection(),!t._loadOk&&!t._loading&&t._loadTries<8&&t._loadFromServer(),t._maybeRebuildDevices(),t._vacTick(),t._activityTick()}const P_=Object.freeze({enabled:!1,z2mBaseTopics:Object.freeze([])});function F_(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^\/+|\/+$/g,"").replace(/\/{2,}/g,"/");return!t||t.length>180||/[#+\u0000-\u001f\u007f]/.test(t)?null:t}function O_(e){const t=e?.zigbee_topology;if(!t||"object"!=typeof t)return P_;const i=t,n=Array.isArray(i.z2m_base_topics)?i.z2m_base_topics:Array.isArray(i.z2mBaseTopics)?i.z2mBaseTopics:[],r=new Set,o=[];for(const e of n){const t=F_(e);if(t&&!r.has(t)&&(r.add(t),o.push(t),8===o.length))break}return{enabled:!0===i.enabled,z2mBaseTopics:o}}function I_(e,t){const i={...e},n=t.z2mBaseTopics.map(F_).filter(Boolean);return t.enabled||n.length?i.zigbee_topology={enabled:!0===t.enabled,z2m_base_topics:[...new Set(n)].slice(0,8)}:delete i.zigbee_topology,i}const E_={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},N_=e=>Math.max(0,Math.min(1,e)),H_=(e,t,i)=>e+(t-e)*i,L_=(e,t,i)=>{const n=1-e;return 3*n*n*e*t+3*n*e*e*i+e*e*e},B_=e=>{const t=N_(e);if(0===t||1===t)return t;let i=0,n=1,r=t;for(let e=0;e<12;e++)r=(i+n)/2,L_(r,.2,.2)0&&e.w>0?t/e.w:1,viewBox:{...e}}}function W_(e,t,i){const n=Math.max(1e-9,e.pixelsPerUnit),r=Math.max(1e-9,t/n),o=Math.max(1e-9,i/n);return{x:e.centerX-r/2,y:e.centerY-o/2,w:r,h:o}}function j_(e){const t=e.trim(),i=/^rgba?\(\s*([\d.]+)[, ]+([\d.]+)[, ]+([\d.]+)(?:\s*[,/]\s*([\d.]+))?\s*\)$/i.exec(t);if(i)return[Number(i[1]),Number(i[2]),Number(i[3]),null==i[4]?1:Number(i[4])];const n=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return n?[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16),8===n.length?parseInt(n.slice(6,8),16)/255:1]:null}function U_(e,t,i){const n=j_(e),r=j_(t);if(!n||!r)return i<.5?e:t;const o=N_(i);return`rgba(${Math.round(H_(n[0],r[0],o))}, ${Math.round(H_(n[1],r[1],o))}, ${Math.round(H_(n[2],r[2],o))}, ${H_(n[3],r[3],o).toFixed(3)})`}class V_{constructor(e,t=E_){this._hooks=e,this._clock=t,this._token=0,this._raf=0,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase||"preparing"===this._state?.phase}get presented(){return this._state?.presented||null}start(e,t,i,n=220){this.cancel(!1);const r=++this._token,o=this._clock.now();if(this._state={token:r,phase:n<=0?"settling":"running",from:e,to:t,presented:n<=0?t:e,startedAt:o,duration:Math.max(0,n),targetMode:i},n<=0)return this._hooks.frame(this._state),this._hooks.settled(this._state),this._state=null,r;this._hooks.frame(this._state);const s=e=>{const t=this._state;if(!t||t.token!==r)return;const i=N_((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:function(e,t,i){const n=B_(i),r=H_(e.stageWidth,t.stageWidth,n),o=H_(e.stageHeight,t.stageHeight,n),s=Math.max(1e-9,e.viewport.pixelsPerUnit),a=Math.max(1e-9,t.viewport.pixelsPerUnit),l={centerX:H_(e.viewport.centerX,t.viewport.centerX,n),centerY:H_(e.viewport.centerY,t.viewport.centerY,n),pixelsPerUnit:Math.exp(H_(Math.log(s),Math.log(a),n))};return{presentedMode:n<.5?e.presentedMode:t.presentedMode,editorChromeHeight:H_(e.editorChromeHeight,t.editorChromeHeight,n),stageWidth:r,stageHeight:o,viewport:{...l,viewBox:W_(l,r,o)},stageColor:U_(e.stageColor,t.stageColor,n),paperColor:U_(e.paperColor,t.paperColor,n),sceneBrightness:H_(e.sceneBrightness,t.sceneBrightness,n),architectureOpacity:H_(e.architectureOpacity,t.architectureOpacity,n),backdropOpacity:H_(e.backdropOpacity,t.backdropOpacity,n),viewWeight:H_(e.viewWeight,t.viewWeight,n),editorWeight:H_(e.editorWeight,t.editorWeight,n),toolbarContentOpacity:H_(e.toolbarContentOpacity,t.toolbarContentOpacity,n)}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(s):(this._raf=0,this._hooks.settled(t),this._state?.token===r&&(this._state=null))};return this._raf=this._clock.requestFrame(s),r}cancel(e=!0){this._raf&&this._clock.cancelFrame(this._raf),this._raf=0;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=t.to,this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}}const G_={now:()=>"undefined"!=typeof performance?performance.now():Date.now(),requestFrame:e=>"function"==typeof requestAnimationFrame?requestAnimationFrame(e):null,cancelFrame:e=>{"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(e)}},K_=e=>Number.isFinite(e);function Y_(e){return!!e&&K_(e.zoom)&&e.zoom>0&&K_(e.viewBox.x)&&K_(e.viewBox.y)&&K_(e.viewBox.w)&&e.viewBox.w>0&&K_(e.viewBox.h)&&e.viewBox.h>0}function X_(e){return{zoom:e.zoom,viewBox:{...e.viewBox}}}function Z_(e,t){return Math.abs(e.zoom-t.zoom)<1e-9&&Math.abs(e.viewBox.x-t.viewBox.x)<1e-6&&Math.abs(e.viewBox.y-t.viewBox.y)<1e-6&&Math.abs(e.viewBox.w-t.viewBox.w)<1e-6&&Math.abs(e.viewBox.h-t.viewBox.h)<1e-6}const J_=(e,t,i)=>e+(t-e)*i,Q_=(e,t,i)=>Math.exp(J_(Math.log(e),Math.log(t),i));class eg{constructor(e,t=G_){this._hooks=e,this._clock=t,this._token=0,this._raf=null,this._state=null}get state(){return this._state}get active(){return"running"===this._state?.phase}get presented(){return this._state?.presented||null}get target(){return this._state?.to||null}start(e,t,i,n){this.cancel(!1);const r=++this._token,o=X_(e),s=X_(t),a=n>0&&Y_(o)&&Y_(s),l=this._clock.now(),c={token:r,phase:a?"running":"settling",from:o,to:s,presented:X_(a?o:s),startedAt:l,duration:a?n:0,reason:i};if(this._state=c,this._hooks.frame(c),!a)return this._settle(r),r;const h=e=>{const t=this._state;if(!t||t.token!==r)return;const i=Math.max(0,Math.min(1,(e-t.startedAt)/t.duration));if(t.presented=i>=1?X_(t.to):function(e,t,i){if(!Y_(e))return X_(t);if(!Y_(t))return X_(e);const n=B_(i);if(n<=0)return X_(e);if(n>=1)return X_(t);const r=e.viewBox.x+e.viewBox.w/2,o=e.viewBox.y+e.viewBox.h/2,s=t.viewBox.x+t.viewBox.w/2,a=t.viewBox.y+t.viewBox.h/2,l=Q_(e.viewBox.w,t.viewBox.w,n),c=Q_(e.viewBox.h,t.viewBox.h,n),h=J_(r,s,n),d=J_(o,a,n);return{zoom:Q_(e.zoom,t.zoom,n),viewBox:{x:h-l/2,y:d-c/2,w:l,h:c}}}(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i>=1)return this._raf=null,void this._settle(r);this._raf=this._clock.requestFrame(h),null===this._raf&&this._settle(r)};return this._raf=this._clock.requestFrame(h),null===this._raf&&(c.phase="settling",c.presented=X_(s),this._hooks.frame(c),this._settle(r)),r}cancel(e=!0){null!==this._raf&&this._clock.cancelFrame(this._raf),this._raf=null;const t=this._state;this._state=null,this._token++,t&&e&&(t.phase="settling",t.presented=X_(t.to),this._hooks.frame(t),this._hooks.settled(t))}dispose(){this.cancel(!1)}_settle(e){const t=this._state;t&&t.token===e&&(t.phase="settling",t.presented=X_(t.to),this._hooks.settled(t),this._state?.token===e&&(this._state=null))}}const tg=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])}),ig=64;function ng(e){return[e.rotDeg,e.tiltDeg,e.xyScale,e.zScale,e.origin[0],e.origin[1]].every(Number.isFinite)&&Math.abs(e.xyScale)>1e-12&&Math.abs(Math.cos(e.tiltDeg*Math.PI/180))>1e-12}function rg(e,t,i=tg){if(!(ng(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const n=i.rotDeg*Math.PI/180,r=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],s=e[1]-i.origin[1],a=(o*Math.cos(n)-s*Math.sin(n))*i.xyScale,l=(o*Math.sin(n)+s*Math.cos(n))*i.xyScale;return[i.origin[0]+a,i.origin[1]+l*Math.cos(r)-t*i.zScale*Math.sin(r)]}function og(e,t=tg){if(!ng(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,n=t.tiltDeg*Math.PI/180,r=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(n));return[t.origin[0]+r*Math.cos(i)+o*Math.sin(i),t.origin[1]-r*Math.sin(i)+o*Math.cos(i)]}function sg(e=tg){return`matrix(${function(e=tg){if(!ng(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,n=e.xyScale*Math.cos(t),r=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),s=e.xyScale*Math.cos(t)*Math.cos(i);return[n,o,r,s,e.origin[0]-n*e.origin[0]-r*e.origin[1],e.origin[1]-o*e.origin[0]-s*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function ag(e,t=tg){const{rect:i,wallHeight:n}=e,r=e.openingHeight??n,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(o)||n<0||r<0||o<0)throw new Error("invalid isometric frame");const s=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],a=Math.max(n,r),l=s.flatMap(e=>[rg(e,-o,t),rg(e,a,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),u=Math.min(...h);return{x:d,y:u,w:Math.max(...c)-d,h:Math.max(...h)-u}}const lg=[".dev",".vacpuck",".oplock",".op-hit",".opening",".rlgo","a","button","input","select","textarea",'[contenteditable]:not([contenteditable="false"])',"[data-room-fit-block]",'[role="link"]','[role="button"]'].join(","),cg=(e,t)=>{try{return"function"==typeof e?.matches&&e.matches(t)}catch{return!1}};function hg(e){for(const t of e){const e=t;if(cg(e,".roomlabel[data-id]")){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(cg(e,lg))return{kind:"interactive"};if(cg(e,'[data-hp="room"][data-id]')){const t=e.getAttribute?.("data-id");return t?{kind:"room",roomId:t}:{kind:"interactive"}}if(cg(e,".stage"))return{kind:"background"}}return{kind:"outside"}}function dg(e){const t=hg(e);return"room"===t.kind?t.roomId:null}class ug{constructor(){this.pointer=null,this.sequence=null}clear(){this.pointer=null,this.sequence=null}clearOutside(e){let t={kind:"outside"};try{t=hg(e.composedPath())}catch{}"outside"===t.kind&&this.clear()}pointerDown(e,t,i){let n={kind:"outside"};try{n=hg(e.composedPath())}catch{}this.pointer=function(e){const t="mouse"===(i=e.pointerType)||"touch"===i||"pen"===i?i:null;var i;return t&&"view"===e.mode&&"background"===e.owner.kind&&!e.blocked&&e.isPrimary&&0===e.button&&Number.isFinite(e.x)&&Number.isFinite(e.y)?{pointerId:e.pointerId,spaceId:e.spaceId,modality:t,x:e.x,y:e.y}:null}({pointerId:e.pointerId,pointerType:e.pointerType,isPrimary:e.isPrimary,button:e.button,spaceId:t,mode:i?"view":"blocked",owner:n,blocked:!i,x:e.clientX,y:e.clientY}),this.pointer||(this.sequence=null)}pointerUp(e,t,i,n,r=Date.now()){let o={kind:"outside"};try{o=hg(e.composedPath())}catch{}const s=function(e,t,i){if(!t||"view"!==i.mode||"background"!==i.owner.kind||i.blocked||t.pointerId!==i.pointerId||t.spaceId!==i.spaceId||!Number.isFinite(i.x)||!Number.isFinite(i.y)||!Number.isFinite(i.now)||Math.abs(i.x-t.x)+Math.abs(i.y-t.y)>=8)return{sequence:null,trigger:!1};const n=e?i.now-e.at:Number.POSITIVE_INFINITY;return e&&e.spaceId===i.spaceId&&e.modality===t.modality&&n>=0&&n<=350?{sequence:null,trigger:!0}:{sequence:{at:i.now,spaceId:i.spaceId,modality:t.modality},trigger:!1}}(this.sequence,this.pointer,{pointerId:e.pointerId,spaceId:t,mode:i?"view":"blocked",owner:o,blocked:n||!i,x:e.clientX,y:e.clientY,now:r});return this.pointer=null,this.sequence=s.sequence,s.trigger}}const pg=e=>e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]);function mg(e){return!!e&&Number.isFinite(e.x)&&Number.isFinite(e.y)&&Number.isFinite(e.w)&&e.w>0&&Number.isFinite(e.h)&&e.h>0}function _g(e,t=0){if(!e.length||!Number.isFinite(t)||t<0||!e.every(pg))return null;let i=1/0,n=1/0,r=-1/0,o=-1/0;for(const t of e)i=Math.min(i,t[0]),n=Math.min(n,t[1]),r=Math.max(r,t[0]),o=Math.max(o,t[1]);const s=r-i,a=o-n;return s>0&&a>0?{x:i-t,y:n-t,w:s+2*t,h:a+2*t}:null}function gg(e,t){return`${e("editor.load_failed")} ${e(t.terminal?"editor.refresh_advice":"editor.retry_advice")}`}class fg extends Error{}class vg{constructor(e){this.options=e,this._state="idle",this._inFlight=null}get state(){return this._state}ensure(){return"ready"===this._state?Promise.resolve(!0):"failed"===this._state?Promise.resolve(!1):(this._inFlight||(this._setState("loading"),this._inFlight=this._loadWithRetry().finally(()=>{this._inFlight=null})),this._inFlight)}async _loadWithRetry(){let e=new Error("Editor runtime did not load"),t=!1;for(const i of[0,1])try{const e=await this.options.load(i);if(e.fingerprint!==this.options.expectedFingerprint)throw new fg(`Editor runtime fingerprint mismatch: expected ${this.options.expectedFingerprint}, got ${e.fingerprint}`);const t=e.create();return this.options.install(t),this._setState("ready"),!0}catch(i){e=i,i instanceof fg&&(t=!0)}return this._setState(t?"failed":"idle"),this.options.failed?.(e,{terminal:t}),!1}_setState(e){e!==this._state&&(this._state=e,this.options.stateChanged?.(e))}}const yg=(e,t)=>{if(!e.has(t))return{hit:!1};const i=e.get(t);return e.delete(t),e.set(t,i),{hit:!0,value:i}},bg=(e,t,i,n)=>{for(e.delete(t),e.set(t,i);e.size>n;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},wg=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),kg=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`,xg="houseplan_card_alpha_v1",Sg=Object.freeze([Object.freeze({id:"iso",issue:89,summary:"Volumetric plan renderer"})]);function $g(e){return/^[a-z][a-z0-9-]*$/.test(e.id)&&Number.isInteger(e.issue)&&e.issue>0&&!!e.summary.trim()}function Mg(e){const t=String(e||"").replace(/^#/,"");return new URLSearchParams(t)}function Cg(e){return Mg(e).get("space")||""}function Rg(e,t,i=Sg){const n=function(e){const t=new Set;for(const i of e){if(!$g(i)||t.has(i.id))return!1;t.add(i.id)}return!0}(i),r=function(e){let t,i=!1;const n=e=>{for(const n of e.getAll("hp_alpha"))i=!0,"1"===n?t=!0:"0"===n&&(t=!1)};return n(new URLSearchParams(String(e.search||"").replace(/^\?/,""))),n(Mg(e.hash)),{present:i,value:t}}(e),o=void 0!==r.value,s="1"===t,a=o?!0===r.value:!r.present&&s,l=n&&a;return{alpha:l,active:Object.freeze(l?i.map(e=>e.id).sort():[]),space:Cg(e.hash),persist:n&&o?l?"1":"0":void 0,knownUrlOperation:o}}let Tg={alpha:!1,active:Object.freeze([]),space:""},Dg=!1,zg="";const Ag=new Set;function Pg(){if("undefined"==typeof window)return Tg;const e=Rg(window.location,function(){try{return window.localStorage.getItem(xg)}catch{return null}}());void 0!==e.persist&&function(e){try{window.localStorage.setItem(xg,e)}catch{}}(e.persist),Tg={alpha:e.alpha,active:e.active,space:e.space},window.__hpAlpha=e.alpha,window.__hpLabs=e.active;for(const e of Ag)e(Tg);return Tg}function Fg(){Pg()}function Og(){if("undefined"==typeof window)return Tg;if(!Dg){Dg=!0,window.__hpLabsListenerCleanup?.(),window.addEventListener("hashchange",Fg),window.addEventListener("popstate",Fg);const e=()=>{window.removeEventListener("hashchange",Fg),window.removeEventListener("popstate",Fg),window.__hpLabsListenerCleanup===e&&delete window.__hpLabsListenerCleanup};window.__hpLabsListenerCleanup=e}return Pg()}const Ig=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Eg(e){let t=0;for(let i=0;iArray.isArray(e)&&Number.isFinite(e[0])&&Number.isFinite(e[1])).map(e=>[Number(e[0]),Number(e[1])]),i=t[t.length-1];return t.length>1&&t[0][0]===i[0]&&t[0][1]===i[1]&&t.pop(),t}(e);if(i.length<3)return[];const n=!t;return Eg(i)>0===n?i:[...i].reverse()}function Hg(e,t,i){return e.length?`M ${e.map(e=>Ig(rg(e,i,t))).join(" L ")} Z`:""}function Lg(e,t,i){const n=t[1]-e[1],r=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return n*Math.sin(o)+r*Math.cos(o)}function Bg(e,t,i=tg){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const n=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=Ng(e,t);if(!i.length)return i;let n=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(Eg(e))>=1e-9)).filter(e=>e.length&&Eg(e[0])>0);n.sort((e,t)=>{const i=e[0][0],n=t[0][0];return i[0]-n[0]||i[1]-n[1]||Math.abs(Eg(t[0]))-Math.abs(Eg(e[0]))});const r=[],o=[];let s=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:r.join(" "),sides:o,componentCount:n.length,edgeCount:s}}function qg(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const Wg=e=>e.every(Number.isFinite);function jg(e,t,i,n){const r=e[0]*i,o=e[1]*n,s=t*Math.PI/180;return[r*Math.cos(s)-o*Math.sin(s),r*Math.sin(s)+o*Math.cos(s)]}function Ug(e,t,i,n,r,o,s){const a=e.flipH?-1:1,l="gate"===e.type?1:e.flipV?-1:1,c=(e.type,e.flipV,e.angle,e.face,{ox:0,oy:0}),h=function(e,t){return[e[0]+t[0],e[1]+t[1]]}([e.x+c.ox,e.y+c.oy],jg(i,e.angle,a,l));return{leaf:t,hinge:h,closedVector:jg(n,e.angle,a,l),quarterVector:jg([-n[1],n[0]],e.angle,a,l),turnDeg:r,bottom:o,top:s}}const Vg=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function Gg(e,t,i=tg){const n=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const r=function(e,t){const i=e.turnDeg*t*Math.PI/180;return[e.hinge[0]+e.closedVector[0]*Math.cos(i)+e.quarterVector[0]*Math.sin(i),e.hinge[1]+e.closedVector[1]*Math.cos(i)+e.quarterVector[1]*Math.sin(i)]}(t,n),o=rg(t.hinge,t.bottom,i),s=rg(r,t.bottom,i),a=rg(r,t.top,i),l=rg(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${Vg(o)} L ${Vg(s)} L ${Vg(a)} L ${Vg(l)} Z`,shadowD:`M ${Vg(rg(t.hinge,0,i))} L ${Vg(rg(r,0,i))}`,depth:Math.max(o[1],s[1])}})}const Kg={window:120,door:90,passage:90,gate:300};function Yg(e){return Kg[e]}function Xg(e,t){return{type:e,lengthCm:Yg(e),flipH:!1,flipV:!1,revision:t}}function Zg(e,t){const i=Math.max(0,e.renderedLength)/2,n=Math.max(0,e.target.physicalHalfWidth),r=n+.18*Math.max(0,t),o=e=>({x1:e,y1:-r,x2:e,y2:r});return{rect:{x:-i,y:n?-n:0,width:2*i,height:2*n},boundaries:[o(-i),o(i)]}}function Jg(e,t){const i=[e[0],e[1]],n=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,n)<=0?{a:i,b:n}:{a:n,b:i}}function Qg(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const n=Jg(e.a,e.b);if(Math.hypot(n.b[0]-n.a[0],n.b[1]-n.a[1])<=1e-9)return;const r=function(e,t){const i=e=>(Math.abs(e)<=5e-10?0:Math.round(1e6*e)/1e6).toFixed(6);return`${i(e[0])},${i(e[1])}>${i(t[0])},${i(t[1])}`}(n.a,n.b),o=t.get(r);if(o)return o.physicalHalfWidth=Math.max(o.physicalHalfWidth,e.half||0),o.sourceOrder=Math.min(o.sourceOrder,i),void(e.partitionHost&&(o.partitionHost&&o.partitionHost.id!==e.partitionHost.id?o.ambiguousPartitionHost=!0:o.partitionHost=e.partitionHost));t.set(r,{segmentKey:r,a:n.a,b:n.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function ef(e,t){if(Math.abs(e.distance-t.distance)>1e-9)return e.distance-t.distance;if(Math.abs(e.perpendicular-t.perpendicular)>1e-9)return e.perpendicular-t.perpendicular;return(e.target.segmentKeyt.target.segmentKey?1:0)||e.target.sourceOrder-t.target.sourceOrder}function tf(e){const t=Qg(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,a=(e[0]-t.a[0])*o+(e[1]-t.a[1])*s,l=Math.max(0,Math.min(r,a)),c=t.a[0]+l*o,h=t.a[1]+l*s;return{along:l,length:r,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*s-(e[1]-t.a[1])*o)}}(e.pointer,t);return{target:t,...i,envelope:Math.max(e.baseTolerance,t.physicalHalfWidth+e.bodyPointerPadding)}}).filter(e=>e.distance<=e.envelope+1e-9).filter(t=>!function(e,t,i,n,r){const o=t.b[0]-t.a[0],s=t.b[1]-t.a[1],a=Math.hypot(o,s);if(!(a>1e-9))return!1;const l=o/a,c=s/a;for(const o of i){if(!o.open&&o.kind)continue;const i=Jg(o.a,o.b),s=i.b[0]-i.a[0],a=i.b[1]-i.a[1],h=Math.hypot(s,a);if(!(h>1e-9))continue;const d=s/h,u=a/h;if(Math.abs(l*u-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>r)continue;const p=(e[0]-i.a[0])*d+(e[1]-i.a[1])*u,m=1e-9;if(!(p<=m||p>=h-m)&&Math.abs((e[0]-i.a[0])*u-(e[1]-i.a[1])*d)<=n+1e-9)return!0}return!1}(e.pointer,t.target,e.intervals,t.envelope,Math.max(1e-9,Math.min(e.baseTolerance,.04*e.gridStep)))),i=t.filter(t=>!!t.target.partitionHost&&e.renderedLength+2*t.target.physicalHalfWidth>t.length+1e-9).sort(ef),n=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(ef);let r=n[0];if(!r)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=n.filter(e=>Math.abs(e.distance-r.distance)<=1e-9&&Math.abs(e.perpendicular-r.perpendicular)<=1e-9),s=o.filter(e=>e.target.partitionHost);if(s.length){if(1!==new Set(s.map(e=>e.target.partitionHost.id)).size||s.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=s[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],n=Math.hypot(t,i),a=t/n,l=i/n;if(!o.every(t=>{const i=t.target.b[0]-t.target.a[0],n=t.target.b[1]-t.target.a[1],r=Math.hypot(i,n),o=i/r,s=n/r,c=Math.abs(a*s-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*a);return c&&h<=1e-9}))return{candidate:null,jambBlockedTarget:null};r=e}if(r.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:a,length:l}=r,c=a.b[0]-a.a[0],h=a.b[1]-a.a[1],d=c/l,u=h/l,p=a.partitionHost?a.physicalHalfWidth:0,m=Math.min(Math.max(0,e.renderedLength)/2+p,l/2);let _=r.along;const g=Math.max(e.gridStep,1e-9),f=l/2;_=Math.abs(_-f)<=g/2?f:Math.round(_/g)*g,_=Math.max(m,Math.min(l-m,_));const v=a.a[0]+d*_,y=a.a[1]+u*_,b=Math.max(0,e.renderedLength)/2,w=_-b,k=_+b,x=Math.max(0,w),S=Math.max(0,l-k),$=[a.a[0]+d*(w-x/2),a.a[1]+u*(w-x/2)],M=[a.a[0]+d*(k+S/2),a.a[1]+u*(k+S/2)];let C=180*Math.atan2(h,c)/Math.PI;C>=90?C-=180:C<-90&&(C+=180);const R=Math.abs(_-f)<=1e-9;return{candidate:{presetRevision:e.preset.revision,geometryRevision:e.geometryRevision,pointer:[e.pointer[0],e.pointer[1]],type:e.preset.type,lengthCm:e.preset.lengthCm,flipH:e.preset.flipH,flipV:e.preset.flipV,x:v,y:y,angle:C,renderedLength:e.renderedLength,target:a,...a.partitionHost?{host:{...a.partitionHost,t:l>0?_/l:0}}:{},measure:{labels:[{distance:x,midpoint:$},{distance:S,midpoint:M}],guide:R?{x:v,y:y,angle:C}:null}},jambBlockedTarget:null}}function nf(e,t,i,n,r=1e-6){return e.presetRevision===i&&e.geometryRevision===n&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=r}function rf(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}let of=0;const sf="1.72.0-beta.2",af="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7",lf=1500,cf=new Map;let hf=0,df=1e4;const uf="houseplan_card_layout_v1",pf="houseplan_card_cfg_v1",mf="houseplan_card_zoom_v1",_f="houseplan_card_nav_v1",gf="houseplan_card_kiosk_v1",ff="houseplan_card_view_v1",vf="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",yf=1e3,bf=(e,t)=>{const i=Math.min(e.x,t.x),n=Math.min(e.y,t.y);return{x:i,y:n,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-n}},wf=(e,t,i)=>{const n=new Event(t,{bubbles:!0,composed:!0});n.detail=i??{},e.dispatchEvent(n)},kf=e=>{history.pushState(null,"",e),wf(window,"location-changed",{replace:!1})},xf=(e,t)=>{let i,n=null;const r=(...r)=>{clearTimeout(i),n=r,i=window.setTimeout(()=>{i=void 0;const t=n;n=null,t&&e(...t)},t)};return r.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=n;n=null,t&&e(...t)},r.cancel=()=>{clearTimeout(i),i=void 0,n=null},r.pending=()=>void 0!==i,r},Sf=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}},$f=["edges","corners"];class Mf extends he{constructor(){super(...arguments),this._editorRuntime=null,this._liveRt=null,this._onboardingRuntime=null,this._editorRuntimeLoadingVisible=!1,this._backdropGuard=null,this._editorModeRequest=0,this._warmModeRequest=0,this._editorRuntimeLoader=new vg({expectedFingerprint:af,load:async e=>{const t=0===e?await import("./houseplan-editor-runtime-C5_zNXOu.js"):await import((()=>{const e=new URL("./houseplan-editor-runtime-C5_zNXOu.js",import.meta.url);return e.searchParams.set("hp_retry",`${sf}-${++of}`),e.href})());return{fingerprint:t.EDITOR_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanEditorRuntime(this)}},install:e=>{this._editorRuntime=e},stateChanged:e=>this._editorRuntimeStateChanged(e),failed:(e,t)=>{console.error("[houseplan] unable to load editor runtime",e),this._showToast(gg(e=>this._t(e),t))}}),this._onboardingRuntimeLoader=new vg({expectedFingerprint:af,load:async e=>{const t=0===e?await import("./houseplan-onboarding-runtime-C0Zl7v7b.js"):await import((()=>{const e=new URL("./houseplan-onboarding-runtime-C0Zl7v7b.js",import.meta.url);return e.searchParams.set("hp_retry",`${sf}-${++of}`),e.href})());return{fingerprint:t.ONBOARDING_RUNTIME_FINGERPRINT,create:()=>new t.HouseplanOnboardingRuntime(this)}},install:e=>{this._onboardingRuntime=e},failed:(e,t)=>{console.error("[houseplan] unable to load onboarding runtime",e),this._showToast(gg(e=>this._t(e),t))}}),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._cfgContentFingerprint="",this._unsubCfg=null,this._unsubLayout=null,this._unsubVirtual=null,this._liveSyncAttempt=null,this._liveSyncGeneration=0,this._liveSyncConnection=null,this._layoutRev=0,this._layoutContentFingerprint="",this._virtualLights=Yc(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._areaRelocationIds=new Set,this._areaSnapshotCleanupCandidates=new Map,this._areaRelocationSyncKey="",this._areaRelocationWrite=Promise.resolve(),this._tip=null,this._hoverRoom=null,this._pointerModality=new __(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...rr},this._decorStyleSeeded=!1,this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._decorImagePalette=null,this._decorAssetCatalog=[],this._decorAssets=new Map,this._decorAssetBusy=!1,this._furnCategory=null,this._furnPreviewInput=null,this._furnTouchPending=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._reducedMotion=!1,this._onMotionChange=e=>{this._reducedMotion=e.matches,this._cancelDevicePressFeedback(),e.matches&&this._cancelCameraTransition(!0),e.matches&&this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:e.matches&&this._modeTransition.active&&this._cancelModeTransition(!0),this.requestUpdate()},this._editorChromeMode="plan",this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest=0,this._modeTransitionTargetZoom=1,this._modeTransitionEditorCamera=null,this._modeTransition=new V_({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._cameraTransitionFit=null,this._cameraTransition=new eg({frame:e=>this._applyCameraTransitionFrame(e),settled:e=>this._settleCameraTransition(e)}),this._editorSecondaryCopy={groupActive:(e,t)=>this._t("editor.group_active",{group:e,item:t}),openGroup:e=>this._t("editor.open_group",{group:e}),disabledAction:(e,t)=>this._t("editor.disabled_action",{action:e,reason:t})},this._tool="draw",this._geometryHistory=new R_(50),this._devicePositionHistory=new R_(50),this._devicePositionBusy=!1,this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._partitionDeleteDialog=null,this._roomDeleteDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCacheValue=null,this._wallUnionPool=new Map,this._isoGeometryCache=new Map,this._isoFallback=new Set,this._openingTunnelCache=null,this._openingWallIndexCache=new Map,this._openingPlacementIntervalsCache=null,this._openingDimensionContextCache=null,this._planSnapGeometryCache=null,this._planStructuralGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._physicalBodiesCache=null,this._lightPhysicalBodiesCache=null,this._cleanFloorCache=new Map,this._innerContourCache=new Map,this._glowRuntimeState=Tm(),this._glowRuntimeHost={window:()=>this.ownerDocument.defaultView||window,isConnected:()=>this.isConnected,requestUpdate:()=>this.requestUpdate(),reducedMotion:()=>this._reducedMotion},this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszLimitViolation=null,this._path=[],this._cursorPt=null,this._planSnapHover=null,this._mergeSel=null,this._openingPreset=null,this._openingRebindId=null,this._openingPresetRevision=0,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._wallFaceGraphCache=[],this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._roomFocus=null,this._roomPointer=null,this._doubleFit=new ug,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._viewportGestureDirty=!1,this._touchContacts=new Map,this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._connectedPath="",this._routeDepartureHandled=!1,this._onLocationChanged=()=>{this._connectedPath&&location.pathname!==this._connectedPath?this._leaveCardRoute():location.pathname===this._connectedPath&&(this._routeDepartureHandled=!1)},this._touchGestureGuard={capture:!0,handleEvent:e=>this._guardTouchGesture(e)},this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootSettling=!1,this._bootSettleRaf=0,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._dangerConfirm=null,this._dangerConfirmController=new ge(e=>{this._dangerConfirm=e}),this._confirmDanger=e=>this._config&&this.hass?"warm"===this._syncDangerConfirmLocaleGate()||this._dangerConfirmMissingSpace()?Promise.resolve(!1):this._dangerConfirmController.confirm(e):Promise.resolve(!1),this._cancelDangerConfirm=()=>{this._dangerConfirmController.cancel()},this._onDangerConfirmDecision=e=>{this._dangerConfirmController.resolve(e.detail.token,e.detail.accepted)},this._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._haSupportApi=null,this._haDecorAssetsApi=null,this._decorAssetSyncToken=0,this._alignDialog=null,this._settingsDialog=null,this._supportDialog=null,this._backupExportDialog=null,this._backupImportDialog=null,this._sunRaysCache=null,this._dayCycleTimer=0,this._dayCycleClockKey="",this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._nativeMoreInfoEntity=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._deviceInboxMemo=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=e=>this._onKey(e),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++hf,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={alpha:!1,active:Object.freeze([]),space:""},this._viewPreference={},this._renderProjection="flat",this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._continuity=this._newContinuityController(),this._continuityHistory=[],this._continuityEpoch=0,this._continuityDataReady=!0,this._continuityPaintToken=-1,this._continuityDisposed=!1,this._renderSnapshotAt=Date.now(),this._hassSequence=0,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotSequence=-1,this._capturedSnapshotDevices=null,this._capturedSnapshotLayout=null,this._capturedSnapshotActivity="",this._capturedSnapshotConfigEpoch=-1,this._capturedSnapshotVirtual="",this._lastValidStageSize=null,this._pendingRefitSize=null,this._refitRaf=0,this._pageVisibility=e=>{if(this._continuity.visibility(e),this._dayCycleVisibility(e),"hidden"===e.kind)return this._clearRoomFocus(!0),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._cancelCameraTransition(!0),void(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0));if(this._vacJumpOnce=!0,!e.long){const e=Date.now();let t=!1;for(const i of this._activityRt.values())!i.flashKind||(i.expiresAt||i.flashTs+h_)>e||(i.flashTs=0,i.flashKind=null,i.expiresAt=0,t=!0);return void(t&&this.requestUpdate())}Date.now()-this._renderSnapshotAt>1e3&&this._continuity.note("device-snapshot-stale"),this._continuityDataReady=!1,this._continuityPaintToken=-1,this._resumeSettling=!0,this._loading?this.requestUpdate():this._loadFromServer()},this._resumeSettling=!1,this._viewportInvalidAt=0,this._vacFit=null,this._vacAllCamerasFor=null,this._vacAllCameraCache=null,this._vacCalConfirm=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._tabDrag=null,this._tabDragRelease=null,this._tabSuppressClick=!1,this._tabOrderWarned=!1,this._onLabsSnapshot=e=>{const t=this._effectiveProjection();this._labs=e;const i=this._effectiveProjection();this._convertProjectionView(t,i),this.requestUpdate()},this._onHashChange=()=>{if(this._hasFixedFloor)return;const e=this._hashSpace();if(e&&this._model.find(t=>t.id===e)&&e!==this._space){if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._selId=null,this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate()}},this._drag=null,this._deviceDrag=null,this._rlResize=null,this._holdFired=!1,this._retryContinuity=()=>{this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.retry(this._continuity.recoveryReason||"plan"),this._loading||this._loadFromServer()},this._cfgEpoch=0,this._cfgEpochPreservedConfig=null,this._terminalFrame=0,this._modelCache=null,this._emptySpaceStateActive=!1,this._decorSnapCache=null,this._markerPreviewMemo=null,this._markerPreviewDevicesMemo=null,this._showHidden=!1,this._connHooked=null,this._connectionWasLost=!1,this._haRegistryConnection=null,this._haRegistryRev=-1,this._haBindingCacheKey="",this._planHassMemo=null,this._renderLife=new z_,this._liveEditorPaintCount=0,this._onHaRegistryUpdate=()=>{const e=mh(this.hass);e.revision===this._haRegistryRev&&this._devices.length||(this._haRegistryRev=e.revision,this._renderLife.invalidate(),this._planHassMemo=null,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate())},this._onConnReady=()=>{if(this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,ph(this.hass),this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(this._onConnReady,400));!this._connectionWasLost&&this._continuity.hasCompleteFrame?this._beginContinuityCandidate("connection-ready",!1,"plan"):(this._continuityDataReady=!1,this._continuityPaintToken=-1),this._loading||this._loadFromServer()},this._onConnLost=()=>{this._booting&&!this._continuity.hasCompleteFrame||(this._connectionWasLost=!0,this._continuityDataReady=!1,this._continuityPaintToken=-1,this._continuity.connectionLost())},this._signer=new Ll(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=xf(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=Qr(this._layout[t]);e&&(this._layout={...this._layout,[t]:e},this._sentPos.set(t,e),this.hass.callWS({type:"houseplan/layout/update",device_id:t,pos:e}).then(e=>this._noteLayoutRev(e)).catch(e=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))).finally(()=>{this._sentPos.get(t)===e&&this._sentPos.delete(t)}))}this._cacheSnapshot()}else this._persistLocalLayout()},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>this._editorRuntimeOrThrow()._undoGeometry(),this._redoGeometry=()=>this._editorRuntimeOrThrow()._redoGeometry(),this._saveConfigDebounced=xf(()=>{this._serverCfg&&this._writeConfig().catch(e=>{const t=!0===e?.physicalGeometryRolledBack;"geometry-unsafe"!==e?.code&&("wall_model_client_outdated"===e?.code?this._showToast(this._t("toast.wall_model_client_outdated")):"conflict"===e?.code?(this._showToast(this._t("toast.conflict")),t||(this._cancelPath(),this._reloadConfigOnly(!0))):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)})),t&&this._reloadRejectedPhysicalWrite())})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>this._editorRuntimeOrThrow()._savePhysicalDialog(),this._deletePhysicalSelection=()=>this._editorRuntimeOrThrow()._deletePhysicalSelection(),this._confirmPartitionDelete=()=>this._editorRuntimeOrThrow()._confirmPartitionDelete(),this._deleteDraftWhole=()=>this._editorRuntimeOrThrow()._deleteDraftWhole(),this._deleteDraftSegment=()=>this._editorRuntimeOrThrow()._deleteDraftSegment(),this._confirmRoomDelete=e=>this._editorRuntimeOrThrow()._confirmRoomDelete(e),this._rebindPartitionOpening=()=>this._editorRuntimeOrThrow()._rebindPartitionOpening(),this._keepClosedAsPartitions=()=>this._editorRuntimeOrThrow()._keepClosedAsPartitions(),this._toggleServerPlans=async()=>this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._toggleServerPlans():this._editorRuntimeOrThrow()._toggleServerPlans(),this._aspectJob=null,this._dayCycleTick=()=>{if(!this.isConnected||"hidden"===this.ownerDocument.visibilityState)return;const e=this._dayCycleState();if(!e)return this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),void(this._dayCycleClockKey="");const t=qn(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(this._editorRuntime)return this._editorRuntime._openSettingsDialog();this._ensureEditorRuntime().then(e=>{e&&this._openSettingsDialog()})},this._openSupportDialog=()=>{this._editorRuntime?this._editorRuntime._openSupportDialog():this._ensureEditorRuntime().then(e=>{e&&this._openSupportDialog()})},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._editorRuntimeOrThrow()._openAlignDialog(),this._toggleOptimizeLivePositions=()=>this._editorRuntimeOrThrow()._toggleOptimizeLivePositions(),this._optimizeUndoBusy=!1,this._openBackupExport=()=>this._editorRuntimeOrThrow()._openBackupExport(),this._openRulesDialog=()=>this._editorRuntimeOrThrow()._openRulesDialog(),this._climateCache=null,this._gearPtCache=new WeakMap}requestUpdate(e,t,i){if("hass"===e&&this.hass&&this._liveRt){const e=this._visibleDeviceSnapshot||this._candidateDeviceSnapshot;if(!this._liveRt.hass(t,this.hass,e?{entityIds:e.entityIds}:null,()=>A_(this)))return}void 0!==e&&(this._terminalFrame=0),this._editorRuntime?._routeLiveEditorUpdate(e,t)||(this._liveRt?.clear(),super.requestUpdate(e,t,i))}_editorRuntimeOrThrow(){if(!this._editorRuntime)throw new Error("Houseplan editor runtime is not loaded");return this._editorRuntime}async _ensureEditorRuntime(){return this._editorRuntimeLoader.ensure()}async _ensureOnboardingRuntime(){return this._onboardingRuntimeLoader.ensure()}_editorRuntimeStateChanged(e){clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,"loading"!==e?(this._editorRuntimeLoadingVisible=!1,this.requestUpdate()):this._editorRuntimeLoadingTimer=window.setTimeout(()=>{this._editorRuntimeLoadingVisible=!0,this.requestUpdate()},150)}async _requestMode(e,t=!0,i=!1){const n=++this._editorModeRequest;if(i&&(this._warmModeRequest=n,this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._pendingRefitSize=null),"view"===e||await this._ensureEditorRuntime()){if(n===this._editorModeRequest&&this.isConnected)return i?(this._adoptMode(e),this._warmRevivePending&&(clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmReviveDialog()),this.requestUpdate(),void this.updateComplete.then(()=>requestAnimationFrame(()=>requestAnimationFrame(()=>{if(this._warmModeRequest!==n||n!==this._editorModeRequest)return;const e=this._stageEl;this._lastValidStageSize=e&&e.clientWidth>0&&e.clientHeight>0?[e.clientWidth,e.clientHeight]:null,this._pendingRefitSize=null,this._warmModeRequest=0})))):void this._setMode(e,t);this._warmModeRequest===n&&(this._warmModeRequest=0)}else this._warmModeRequest===n&&(this._warmModeRequest=0)}_seedDecorStyle(e){if(this._decorStyleSeeded||!e)return;this._decorStyleSeeded=!0;const t=e.settings?.decor_default_style;t&&(this._decorStyle=function(e,t){if(!e||"object"!=typeof e)return{...t};const i=e,n="string"==typeof i.color&&i.color?i.color:t.color,r="string"==typeof i.fill_color&&i.fill_color?i.fill_color:t.fillColor,o=Number(i.width_cm);return{color:n,opacity:or(i.opacity,t.opacity),widthCm:Number.isFinite(o)&&o>0?Math.max(.1,Math.min(100,o)):t.widthCm,fill:"boolean"==typeof i.fill?i.fill:t.fill,fillColor:r,fillOpacity:or(i.fill_opacity,t.fillOpacity)}}(t,rr))}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}get _modeTransitionBusy(){return this._modeTransitionPreparing||this._modeTransition.active}get _doubleFitEnabled(){return!("view"!==this._mode||this._vacFit||this._modeTransitionBusy||this._touchSequenceMultitouch||"steady"!==this._continuity.state||this._continuity.overlayBlocksInteraction)}_cameraState(){const e=this._viewOr(this._baseVb());return{zoom:this._zoom,viewBox:{...e}}}_normalizeCameraState(e){const t=this._cameraTransitionFit||vi(this._baseVb(),this._stageAspect());return{zoom:Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,e.zoom)),viewBox:this._clampView({...e.viewBox},t)}}_applyCameraTransitionFrame(e){const t=this._normalizeCameraState(e.presented);e.presented=t,this._zoom=t.zoom,this._view={...t.viewBox},this._liveVp(!0)}_settleCameraTransition(e){const t=this._normalizeCameraState(e.to);this._zoom=t.zoom,this._view={...t.viewBox},this._cameraTransitionFit=null,"room"!==e.reason&&this._saveZoom(),this.requestUpdate()}_cancelCameraTransition(e=!1,t=!1){const i=this._cameraTransition.state?.reason,n=t&&this._cameraTransition.active?this._cameraTransition.presented?.zoom:void 0;this._cameraTransition.cancel(e),this._cameraTransitionFit=null,void 0!==n&&"room"!==i&&this._saveZoom()}_prepareCameraCommand(){this._modeTransitionBusy&&this._cancelModeTransition(!0),"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1))}_startCameraTransition(e,t,i,n){const r=this._cameraState(),o=this._cameraTransition.target;return(!o||!Z_(o,e))&&(Z_(r,e)?(this._cancelCameraTransition(!1),!1):(this._cameraTransitionFit={...t},this._cameraTransition.start(r,e,i,this._reducedMotion?0:n),!0))}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const n=document.createElement("span");n.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(n);const r=getComputedStyle(n).color||t;return n.remove(),r}_currentModeVisual(e=this._mode){const t=this._modeTransition.presented||this._modeTransitionVisual;if(t)return{...t,viewport:{...t.viewport,viewBox:{...t.viewport.viewBox}}};const i=this._stageEl;if(!i||i.clientWidth<=0||i.clientHeight<=0)return null;const n=this.renderRoot.querySelector(".editorchrome"),r=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),s=this.renderRoot.querySelector(".hp-backdrop"),a=this.renderRoot.querySelector(".zoomwrap"),l=a?getComputedStyle(a).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:n?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:q_(r,i.clientWidth),stageColor:getComputedStyle(i).backgroundColor||"rgb(255, 255, 255)",paperColor:o?getComputedStyle(o).fill:"rgb(255, 255, 255)",sceneBrightness:Number.isFinite(c)?c:1,architectureOpacity:"decor"===e?.35:1,backdropOpacity:s&&Number(getComputedStyle(s).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,n,r){const o=vi(this._baseVb(),n/Math.max(1,r)),s=Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,e)),a=o.w/s,l=o.h/s,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-a/2,y:h-l/2,w:a,h:l},o)}_targetStageColor(e){return"view"!==e?"rgb(255, 255, 255)":this._cssColor(this._stageBg(this._spaceDisplayForRender()),this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"))}_targetPaperColor(e){return"view"===e&&this._spaceModel()?.bg?this._cssColor("var(--ha-card-background, var(--card-background-color, #111))","rgb(17, 17, 17)"):"rgb(255, 255, 255)"}_targetBrightness(e){return 1}_applyModeTransitionFrame(e){e.targetMode===this._mode&&(this._modeTransitionVisual=e.presented,this._view={...e.presented.viewport.viewBox},this.requestUpdate())}_settleModeTransition(e){if(e.targetMode!==this._mode)return;const t=this._modeTransitionRequest;this._view={...e.to.viewport.viewBox},this._zoom=this._modeTransitionTargetZoom,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._lastValidStageSize=[e.to.stageWidth,e.to.stageHeight],"view"===this._mode&&(this._saveZoom(),this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate(),this.updateComplete.then(()=>{if(!this.isConnected||t!==this._modeTransitionRequest||this._modeTransitionBusy||e.targetMode!==this._mode)return;const i=this._stageEl;if(i&&i.clientWidth>0&&i.clientHeight>0&&(Math.abs(i.clientWidth-e.to.stageWidth)>.5||Math.abs(i.clientHeight-e.to.stageHeight)>.5)){const e=this._view;this._lastValidStageSize=[i.clientWidth,i.clientHeight],this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}const n=this.renderRoot.activeElement;(!n||!n.isConnected||!!n.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){this._cancelCameraTransition(!1);const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){e!==this._mode&&this._clearRoomFocus(!0),this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,n){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const r=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,n,r,o),this._lastValidStageSize=[r,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,n,r,o){this.updateComplete.then(()=>{if(!this.isConnected||e!==this._modeTransitionRequest||this._mode!==i)return void(e===this._modeTransitionRequest&&(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this.requestUpdate()));const s=this.renderRoot.querySelector(".editorchrome"),a=s?.querySelector(".editorchrome-inner"),l="view"===i?0:a?.scrollHeight||a?.getBoundingClientRect().height||0,c=Math.max(1,t.stageHeight+t.editorChromeHeight),h=Math.max(1,c-l),d=this._stageEl?.clientWidth||t.stageWidth;if(d<=0||h<=0)return this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._applyView(n,r,o),void this.requestUpdate();const u=this._viewForModeTarget(n,r,o,d,h),p={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:q_(u,d),stageColor:this._targetStageColor(i),paperColor:this._targetPaperColor(i),sceneBrightness:this._targetBrightness(i),architectureOpacity:"decor"===i?.35:1,backdropOpacity:"decor"===i&&"backdrop"!==this._decorTool?.5:1,viewWeight:"view"===i?1:0,editorWeight:"view"===i?0:1,toolbarContentOpacity:"view"===i?0:1};this._modeTransitionPreparing=!1;const m=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,p,i,this._reducedMotion||m?0:220)})}get _hasFixedFloor(){return!!this._config&&Object.prototype.hasOwnProperty.call(this._config,"floor")}_fixedFloorState(e=this._model,t=this._loadOk){const i=this._config?.floor,n=function(e){if(!e.hasFloor)return{kind:"absent"};const t=e.floor;if("string"==typeof t)return t.length?e.spaceIds.includes(t)?{kind:"valid",id:t,source:"id"}:{kind:"invalid",reason:"unknown-id",value:t}:{kind:"invalid",reason:"empty-id",value:t};if("number"==typeof t){if(!Number.isFinite(t))return{kind:"invalid",reason:"non-finite-index",value:t};if(!Number.isInteger(t))return{kind:"invalid",reason:"fractional-index",value:t};if(t<0)return{kind:"invalid",reason:"negative-index",value:t};const i=e.spaceIds[t];return void 0===i?{kind:"invalid",reason:"out-of-range-index",value:t}:{kind:"valid",id:i,source:"index"}}return{kind:"invalid",reason:"invalid-type",value:t}}({spaceIds:e.map(e=>e.id),hasFloor:this._hasFixedFloor,floor:i});return!this._hasFixedFloor||t?n:"number"==typeof i?"valid"===n.kind||"invalid"===n.kind&&"out-of-range-index"===n.reason?{kind:"pending",value:i}:n:"valid"===n.kind||"invalid"===n.kind&&"unknown-id"!==n.reason?n:{kind:"pending",value:i}}_canCommitSpace(e,t=!1){if(t||!this._hasFixedFloor)return!0;const i=this._fixedFloorState();return"valid"===i.kind&&i.id===e}_commitSpace(e,t=!1){return!!this._canCommitSpace(e,t)&&(e!==this._space&&(this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._cancelCameraTransition(!1),this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._editorRuntime?._clearFurniturePreview()),this._space=e,!0)}_slideTo(e,t){if(e===this._space)return!0;if(!this._canCommitSpace(e))return!1;if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return!1;this._cancelModeTransition(!0);const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;return this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._commitSpace(e),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._editorSecondary?.closeForNavigation(),this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=t,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate()),!0}get _canReorderTabs(){return rf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){rf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(Sf(e),this._tabDragRelease=e=>this._tabPointerUp(e),window.addEventListener("pointerup",this._tabDragRelease),window.addEventListener("pointercancel",this._tabDragRelease),this._tabDrag={id:t,pointerId:e.pointerId,x:e.clientX,y:e.clientY,moved:!1,targetId:null,placement:null})}_tabDropTargetAt(e,t,i){const n=this._model.map(e=>e.id),r=n.indexOf(i);if(r<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const s of o){const o=s.dataset.id||"";if(!o||o===i)continue;const a=s.getBoundingClientRect();if(ea.right||ta.bottom)continue;const l=n.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,n;const r=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===r?.targetId&&t.placement===r?.placement||(this._tabDrag={...t,moved:!0,targetId:r?.targetId||null,placement:r?.placement||null})}_tabPointerUp(e){const t=this._tabDrag;if(t&&t.pointerId!==e.pointerId)return;const i="pointerup"===e.type&&t?.moved?this._tabDropTargetAt(e.clientX,e.clientY,t.id):null;"pointerup"===e.type&&t?.moved&&this._suppressNextTabClick(),this._endTabDrag(),t?.moved&&i&&this._commitTabOrder(t.id,i.targetId)}_endTabDrag(){this._tabDrag=null,this._tabDragRelease&&(window.removeEventListener("pointerup",this._tabDragRelease),window.removeEventListener("pointercancel",this._tabDragRelease),this._tabDragRelease=null)}_tabClick(e){if(this._tabSuppressClick)return this._tabSuppressClick=!1,clearTimeout(this._tabSuppressClickTimer),void(this._tabSuppressClickTimer=void 0);this._pickSpace(e)}_suppressNextTabClick(){this._tabSuppressClick=!0,clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=window.setTimeout(()=>{this._tabSuppressClick=!1,this._tabSuppressClickTimer=void 0},0)}_commitTabOrder(e,t){const i=this._serverCfg;if(!i||!this._canReorderTabs)return;const n=this._model.map(e=>e.id),r=function(e,t,i){const n=e.indexOf(t),r=e.indexOf(i);if(n<0||r<0||n===r)return e;const o=e.slice();return o.splice(n,1),o.splice(r,0,t),o}(n,e,t);if(r===n)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),s=function(e,t,i,n=()=>""){if(!i)return[];const r=[];for(const o of e){if(!o||!0===o.removed)continue;const e="string"==typeof o.id?o.id:"";if(!e)continue;if("string"==typeof o.space&&o.space)continue;const s=("string"==typeof o.area?o.area:"")||n(e)||"";s&&t[s]||r.push({id:e,space:i})}return r}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),n[0]||"",e=>o.get(e)||"");if(s.length){const e=new Map(s.map(e=>[e.id,e.space]));for(const t of i.markers||[]){const i=e.get(String(t.id));i&&(t.space=i)}}i.spaces=function(e,t){const i=new Map(t.map((e,t)=>[e,t]));return e.map((e,t)=>({space:e,index:t})).sort((e,t)=>(i.get(String(e.space?.id))??Number.MAX_SAFE_INTEGER)-(i.get(String(t.space?.id))??Number.MAX_SAFE_INTEGER)||e.index-t.index).map(e=>e.space)}(i.spaces||[],r),this._saveConfig(),this._tabOrderWarned||(this._tabOrderWarned=!0,this._showToast(this._t("toast.space_order_changed")))}_pickSpace(e){if(this._endTabDrag(),e===this._space)return;const t=this._model.map(e=>e.id),i=t.indexOf(this._space),n=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&n0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const e=this._model.map(e=>e.id),t=e.indexOf(this._space);this._slideTo(e[(t+1)%e.length],"left"),this._showKioskDots()}}_syncCycleTimer(){clearInterval(this._cycleTimer),this._cycleTimer=void 0,this.isConnected&&!this._hasFixedFloor&&this._config?.kiosk&&Number(this._config.cycle)>0&&(this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle)))}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}get _wallUnionCache(){return this._wallUnionCacheValue}set _wallUnionCache(e){this._wallUnionCacheValue=e,null===e&&this._wallUnionPool.clear()}get _glowClipCache(){return this._glowRuntimeState.clipCache}get _glowGeometryWarnings(){return this._glowRuntimeState.geometryWarnings}get _glowFeatherUnits(){return this._glowRuntimeState.featherUnits}set _glowFeatherUnits(e){this._glowRuntimeState.featherUnits=e}get _glowRenderedSources(){return this._glowRuntimeState.renderedSources}get _glowLastAppearance(){return this._glowRuntimeState.lastAppearance}get _glowFeatherSuspendUntil(){return this._glowRuntimeState.featherSuspendUntil}set _glowFeatherSuspendUntil(e){this._glowRuntimeState.featherSuspendUntil=e}get _glowFeatherResumeTimer(){return this._glowRuntimeState.featherResumeTimer}set _glowFeatherResumeTimer(e){this._glowRuntimeState.featherResumeTimer=e}get _glowSourceSeq(){return this._glowRuntimeState.sourceSeq}set _glowSourceSeq(e){this._glowRuntimeState.sourceSeq=e}_syncDangerConfirmLocaleGate(){return this._config&&this.hass?uu(this,fu,xu(this.hass,this._config.language)):"ready"}_dangerConfirmMissingSpace(){const e=this._model;if(!e.length)return!1;const t=this._fixedFloorState(e);return"pending"!==t.kind&&"invalid"!==t.kind&&!this._spaceModel()}_newContinuityController(){return new Il(()=>{this._resumeSettling="steady"!==this._continuity.state,this._continuityEpoch++,this.isConnected&&this.requestUpdate()})}get _labsIso(){return this._labs.active.includes("iso")}get _desiredProjection(){return"view"===this._mode&&this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat"}_saveViewPreference(){try{localStorage.setItem(ff,JSON.stringify(this._viewPreference))}catch{}}_logicalViewCenter(e){const t=this._view;if(!t)return null;const i=[t.x+t.w/2,t.y+t.h/2],n="iso"===e?og(i):i;return{x:n[0],y:n[1]}}_convertProjectionView(e,t){if(e===t)return;this._clearRoomFocus(!0);const i=this._logicalViewCenter(e);this._view=null;const n=i?"iso"===t?rg([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,n?.[0],n?.[1]),this._warmPatch({vp:this._warmViewportState()}),this.requestUpdate()}_setProjection(e){if(!this._labsIso||"view"!==this._mode)return;const t=this._effectiveProjection();if(this._viewPreference={...this._viewPreference,[this._space]:e},"iso"===e){const e=this._isoSceneKey();e&&this._isoFallback.delete(e)}this._saveViewPreference();const i=this._effectiveProjection();this._convertProjectionView(t,i)}_hashSpace(){return Cg(window.location.hash||"")}connectedCallback(){this._connectedPath=location.pathname,this._routeDepartureHandled=!1,window.addEventListener("location-changed",this._onLocationChanged),window.addEventListener("popstate",this._onLocationChanged),this._continuityDisposed&&(this._continuity=this._newContinuityController(),this._continuityDisposed=!1,this._continuityPaintToken=-1);const e=b_(this.ownerDocument);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=Fl(this.ownerDocument,this._pageVisibility),this._languageFailureUnsub?.(),this._languageFailureUnsub=(t=()=>{this._showToast(this._t("toast.locale_load_failed"))},gu.add(t),()=>gu.delete(t)),super.connectedCallback(),this._ensureLiveRuntime().catch(()=>this.requestUpdate()),this._pointerModality.connect(this.ownerDocument.defaultView);const i=this.ownerDocument.defaultView?.MutationObserver;i&&(this._pointerHoverObserver=new i(e=>{for(const t of e)for(const e of t.addedNodes)this._syncPointerHoverSubtree(e)}),this._pointerHoverObserver.observe(this.renderRoot,{childList:!0,subtree:!0})),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),y_(this.ownerDocument).then(e=>{e!==this._glowScreenBlend&&(this._glowScreenBlend=e,this.isConnected&&this.requestUpdate())}),this.hass&&this._ensureHaRegistryAuthority(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>this._referencedContentUrls()),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e){Ag.add(e);const t=Og();return"undefined"==typeof window&&e(t),()=>{Ag.delete(e)}}(this._onLabsSnapshot),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},lf)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._loadOk&&this._ensureLiveSyncSubscriptions(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0)),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){this._liveRt?.dispose(),this._editorRuntime?._disposeLiveEditor(),this._clearRoomFocus(!0),this._cancelDangerConfirm(),this._connectedPath&&location.pathname!==this._connectedPath&&this._leaveCardRoute(),window.removeEventListener("location-changed",this._onLocationChanged),window.removeEventListener("popstate",this._onLocationChanged),this._continuityUnsub?.(),this._continuityUnsub=void 0,this._languageFailureUnsub?.(),this._languageFailureUnsub=void 0,this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._refitRaf&&(cancelAnimationFrame(this._refitRaf),this._refitRaf=0),this._warmModeRequest=0,this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0),this._dayCycleClockKey="",this._bootSettleRaf&&(cancelAnimationFrame(this._bootSettleRaf),this._bootSettleRaf=0),this._bootSettling=!1;for(const e of this._activityRt.values())clearTimeout(e.timer);window.removeEventListener("keydown",this._keyHandler),this._endTabDrag(),clearTimeout(this._tabSuppressClickTimer),this._tabSuppressClickTimer=void 0,this._tabSuppressClick=!1,clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),this._connHooked=null,this._haRegistryRelease?.(),this._haRegistryRelease=void 0,this._haRegistryConnection=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._editorRuntimeLoadingTimer),this._editorRuntimeLoadingTimer=void 0,this._editorRuntimeLoadingVisible=!1,this._modeTransition.dispose(),this._cameraTransition.dispose(),this._cameraTransitionFit=null,this._modeTransitionVisual=null,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransitionRequest++,this._slideTimer=void 0,this._slide="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),this._cancelDeviceDrag(),window.removeEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=void 0,clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),this._unsubVirtual&&(this._unsubVirtual(),this._unsubVirtual=null),this._unsubTrail&&(this._unsubTrail(),this._unsubTrail=void 0),this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=null,clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),qm(this._glowRuntimeState,this._glowRuntimeHost),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._touchContacts.clear(),this._touchSequenceMultitouch=!1,this._touchClickBlockUntil=0,this._clearTransientHover(!0),this._cancelDevicePressFeedback(),this._pointerHoverObserver?.disconnect(),this._pointerHoverObserver=void 0,this._pointerModality.disconnect(),this._editorSecondary?.reset(),this._resumeSettling=!1,this._continuityHistory=[...this._continuityHistory,...this._continuity.trace].slice(-80),this._continuity.dispose(),this._continuityDisposed=!0,super.disconnectedCallback()}_onKey(e){if("Escape"===e.key&&this._vacFit){if(this._vacFit.busy)return;return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void e.stopPropagation()}if("Escape"===e.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._vacCalConfirm)return void(this._vacCalConfirm.busy||(this._vacCalConfirm=null));if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void this._closeInfoCard();if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return this._alignDialog=null,void(this._preflightClipboardFallback=null);if(this._backupImportDialog)return void(this._backupImportDialog=null);if(this._backupExportDialog)return void(this._backupExportDialog=null);if(this._supportDialog)return void this._editorRuntime?._closeSupportDialog();if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void this._closeMarkerDialog();if(this._deviceInbox)return void(this._deviceInbox=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0);if(this._editorSecondary?.hasOpenGroup)return e.preventDefault(),void this._editorSecondary?.closeGroup(!0)}const t=e.composedPath?.()||[e.target],i=t.some(e=>e?.matches?.('input, textarea, select, [contenteditable="true"]')),n=t.some(e=>e?.classList?.contains?.("editor-secondary")),r=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),s=/^[a-z]$/.test(o),a="z"===o||!s&&"KeyZ"===e.code,l="y"===o||!s&&"KeyY"===e.code,c=r&&(a&&e.shiftKey||l),h=r&&a&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry();if(("Delete"===e.key||"Backspace"===e.key)&&this._decorSel&&!i&&!n)return e.preventDefault(),void this._decorDeleteSel();const t="ArrowLeft"===e.key?[-this._gridPitch,0]:"ArrowRight"===e.key?[this._gridPitch,0]:"ArrowUp"===e.key?[0,-this._gridPitch]:"ArrowDown"===e.key?[0,this._gridPitch]:null;return!t||r||e.altKey||"select"!==this._decorTool||!this._decorSel||!this._decorList.some(e=>e.id===this._decorSel)||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag||i||n||this._editorSecondaryDialogBlocked?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._editorRuntime?._clearFurniturePreview(),this._editorRuntime?._furnShiftDetach(),this._furnPalette=null,this._furnCategory=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorNudge(t[0],t[1]))}if("devices"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoDevicePosition()):h?(e.preventDefault(),void this._undoDevicePosition()):void("Escape"===e.key&&this._deviceDrag&&(e.preventDefault(),this._cancelDeviceDrag()))}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!n)return e.preventDefault(),void this._deletePhysicalSelection();if(c)return e.preventDefault(),void this._redoGeometry();if(h)return e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():this._wallFaceBatch?(this._roomDialogCancel(),void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint())):"draw"===this._tool&&this._path.length?void(this._activeDraftId&&this._path.length>1?this._undoActiveDraftPoint():this._undoPoint()):"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry();if("Escape"===e.key)return this._physicalDrag||this._physicalRotate?(e.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(e.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length?(e.preventDefault(),void this._finishWallChain()):this._physicalSel?(e.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(e.preventDefault(),this._resize?.dragging?void this._rszCancelDrag():("exit-tool"===this._resize?.escapeIdle()&&(this._tool="draw"),void this.requestUpdate())):"split"===this._tool?(e.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(e.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(e.preventDefault(),void(this._wallDialog=null)):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"column"!==this._tool||(e.preventDefault(),"opening"===this._tool&&this._clearOpeningPlacement(!0),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const e=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const t=this._curSpaceCfg,i=(t.room_drafts||[]).findIndex(e=>e.id===this._activeDraftId);if(i>=0){const n=Array.isArray(t.room_drafts[i]?.segments)?t.room_drafts[i].segments:[];this._path.length<2?(t.room_drafts.splice(i,1),t.room_drafts.length||delete t.room_drafts,this._activeDraftId=null):t.room_drafts[i]={id:this._activeDraftId,points:this._path.map(e=>[e[0]/yf,e[1]/yf]),segments:this._draftSegmentCms.map((e,t)=>({...n[t]||{},cm:e}))},this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),e)}return}this._path=this._path.slice(0,-1)}}_undoActiveDraftPoint(){const e=this._activeDraftId,t=this._path[0]?[...this._path[0]]:null,i=this._geometryHistory.undo();if(!i)return void this._undoPoint();if(!this._applyGeometryState(i.before,!0))return void this._geometryHistory.clear();const n=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;n?(this._activeDraftId=n.id,this._path=n.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,n.segments.map(e=>e.cm),n.id),this._resumeDraftBySpace[this._space]=n.id):(this._activeDraftId=null,this._path=t?[t]:[],this._draftSegmentCms=[],e&&this._resumeDraftBySpace[this._space]===e&&delete this._resumeDraftBySpace[this._space]),this._clearPlanSnapHover(),this._showToast(this._t("history.undone",{name:i.name}))}static async getConfigElement(){return await import("./editor-BVOuJXsz.js"),document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of cf.values())for(const t of e)clearTimeout(t.evict);cf.clear(),df=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const n of cf.values())for(const r of n)e++,r.dlg&&(t++,i.push(r.dlg.kind));return{keys:cf.size,slots:e,dlgs:t,drafts:i}}setConfig(e){const t=this._config,i=!!t&&Object.prototype.hasOwnProperty.call(t,"floor");this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...e};(i!==this._hasFixedFloor||t?.floor!==this._config.floor)&&(this._hashApplied=!1,this._navApplied=!1,this._warmVpArmed=!1),this._config.kiosk&&(this._booting=!1,this._bootFading=!1),!this._hasFixedFloor&&e.default_floor&&this._commitSpace(e.default_floor,!0);try{this._zoomBySpace=JSON.parse(localStorage.getItem(mf)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(ff)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=Og();try{const e=JSON.parse(localStorage.getItem(gf)||"null");this._kioskScale={icon:kn(e?.icon),font:kn(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(pf)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._seedDecorStyle(this._serverCfg),this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||El(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||El(this._layout),this._virtualLights=Yc(e.virtual_lights,this._cfgRev),this._serverStorage=!0)}catch{}this._adoptInitialSpace(this._model,this._loadOk),"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._syncCycleTimer(),this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const e=kg(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),n=cf.get(e);if(!n||!n.length)return;const r=n.find(e=>e.owner===this._warmGen);if(r)return this._warmLongReturn=!!r.freed&&Date.now()-r.freed>=zl,clearTimeout(r.evict),r.evict=0,r.freed=0,r.live=!0,this._warmSlot=r,this._warmKey=e,r.frameFingerprint&&this._continuity.adoptCompleteFrame(r.frameFingerprint),void(!this._devices.length&&r.devices?.length&&(this._devices=[...r.devices]));const{slot:o,sure:s}=((e,t,i,n)=>{const r=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===n?4:e.live?0:t?3:2};let o=null,s=0,a=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=r(i);e<=0||(e>s?(o=i,s=e,a=1):e===s&&a++)}return!o||a>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(n,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=zl,this._booting=!1,this._bootFading=!1,this._hdrH=o.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},lf)),this._warmKey=e,s?(clearTimeout(o.evict),o.evict=0,o.owner=this._warmGen,o.place=t?new WeakRef(t):null,o.idx=i,o.live=!0,this._warmSlot=o,this._warmVp=o.vp,o.frameFingerprint&&this._continuity.adoptCompleteFrame(o.frameFingerprint),!this._devices.length&&o.devices?.length&&(this._devices=[...o.devices]),this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,path:location.pathname,place:t?new WeakRef(t):null,idx:i,live:!0,hdrH:o.hdrH,stageH:o.stageH,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0},n.push(this._warmSlot),this._warmTrim(n)))}_warmIdx(e){const t=e?.children;if(!t)return-1;for(let e=0;e4;){const t=e.findIndex(e=>!e.live);if(t<0)break;clearTimeout(e[t].evict),e.splice(t,1)}}_warmScheduleEvict(e,t){if(clearTimeout(e.evict),!(e.dlg||e.vp&&"view"!==e.vp.mode))return;const i=e.freed,n=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==n)return;var r;e.dlg=null,e.vp=(r=e.vp)&&"view"!==r.mode?{...r,mode:"view",zoom:r.snap?.space===r.space?r.snap.zoom:r.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:r,e.frameFingerprint="";const o=cf.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},df+250)}_warmAdoptViewport(e){const t=this._warmVp;if(!t||this._warmSlot?.path!==location.pathname)return void(this._warmVp=null);const i=this._fixedFloorState();if(this._hashApplied||!this._model.find(e=>e.id===t.space)||"valid"===i.kind&&i.id!==t.space||this._hasFixedFloor&&"valid"!==i.kind)return void(this._warmVp=null);this._commitSpace(t.space,!0),this._navApplied=!0;const n="view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view";this._adoptMode("view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const r=this._effectiveProjection(),o=r===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=o&&t.view?{...t.view}:null,this._viewModeSnap=o&&t.snap?{...t.snap}:null,!o&&t.logicalCenter){const e="iso"===r?rg([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var s;this._tool="opening"===(s=function(e){return"partition"===e?"draw":e}(s=t.tool))?"draw":"string"==typeof s&&wg.has(s)?s:"draw",this._decorTool=t.decorTool,this._showHidden=t.showHidden,this._showFar!==t.showFar&&(this._showFar=t.showFar,this._frame=null),this._selId=t.selId,this._resize?.restoreSelection(t.rszSel),this._decorSel=t.decorSel,this._warmVpArmed=!0,"view"!==n&&this._requestMode(n,!1,!0)}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=kg(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!t)return;const e=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,path:location.pathname,place:e?new WeakRef(e):null,idx:this._warmIdx(e),live:!0,hdrH:this._hdrH,stageH:0,vp:null,frameFingerprint:"",devices:null,dlg:null,freed:0,evict:0};const n=cf.get(i)||[];for(n.push(this._warmSlot),cf.set(i,n),this._warmTrim(n);cf.size>8;){const e=cf.keys().next().value;if(void 0===e||e===i)break;for(const t of cf.get(e)||[])clearTimeout(t.evict);cf.delete(e)}}Object.assign(this._warmSlot,e)}}_warmViewportState(){const e=this._effectiveProjection();return{space:this._space,mode:this._mode,projection:e,activeLabsIso:this._labsIso,logicalCenter:this._logicalViewCenter(e),zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._resize?.selectedRoomId,decorSel:this._decorSel}}_warmDialogState(){const e=(e,t)=>({kind:e,space:this._space,mode:this._mode,data:t});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog||this._backupExportDialog||this._backupImportDialog?null:this._openingInfo?e("openingInfo",this._openingInfo.id):this._infoCard?e("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:e("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:e("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:e("marker",this._markerDialog):this._openingDialog?e("opening",this._openingDialog):this._backdropDialog?e("backdrop",this._backdropDialog):this._decorShapeDialog?e("decorShape",this._decorShapeDialog):this._decorTextDialog?e("decorText",this._decorTextDialog):this._roomDialog?e("room",{editId:this._roomEditId,fill:this._roomFill,customFill:this._roomCustomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,wallFaceBatch:this._wallFaceBatch,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:e("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._config?.kiosk||"steady"!==this._continuity.state)return;const e={vp:this._warmViewportState(),frameFingerprint:this._continuity.frameFingerprint,devices:this._devices};if(this._warmRevivePending||(e.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const t=this.parentNode;e.place=t?new WeakRef(t):null,e.idx=this._warmIdx(t)}this._warmPatch(e)}_warmReviveDialog(){const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return void(this._warmRevivePending=!1);const t=e.dlg,i=e.freed;if(t.mode===this._mode||"view"===t.mode||this._warmVp?.mode!==t.mode||this._editorRuntime){if(this._warmRevivePending=!1,e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>df)&&t.space===this._space&&t.mode===this._mode){switch(t.kind){case"space":this._spaceDialog={...t.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...t.data,busy:!1};break;case"settings":this._settingsDialog={...t.data,busy:!1};break;case"rules":this._rulesDialog={...t.data,busy:!1};break;case"opening":this._openingDialog={...t.data};break;case"backdrop":this._backdropDialog={...t.data};break;case"decorShape":this._decorShapeDialog={...t.data};break;case"decorText":{this._decorTextDialog={...t.data};const e=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:e,end:e};break}case"room":{const e=t.data;this._roomEditId=e.editId,this._roomFill=e.fill,this._roomCustomFill=e.customFill||null,this._roomTempSrc=e.tempSrc,this._roomHumSrc=e.humSrc,this._roomSrcOpen=e.srcOpen,this._roomSrcFilter=e.srcFilter,this._roomNameScale=e.nameScale,this._roomLabelScale=e.labelScale,this._areaSel=e.areaSel,this._nameSel=e.nameSel,this._pendingSplit=e.pendingSplit,this._wallFaceBatch=e.wallFaceBatch||null,this._path=e.path,this._wallFaceBatch&&(this._activeDraftId=this._wallFaceBatch.activeDraftId,this._draftSegmentCms=[...this._wallFaceBatch.activeCms]),this._roomDialog=!0;break}case"info":{const e=this._devices.find(e=>e.id===t.data);e&&(this._infoCard=e);break}case"openingInfo":{const e=(this._curSpaceCfg?.openings||[]).find(e=>e.id===t.data);e&&(this._openingInfo=e);break}}this.requestUpdate()}}else this._warmRevivePending=!0}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=El(this._serverCfg),this._layoutContentFingerprint=El(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const n=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(Qc).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>n.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(pf,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,config_fingerprint:this._cfgContentFingerprint,layout:this._layout,layout_rev:this._layoutRev,layout_fingerprint:this._layoutContentFingerprint,virtual_lights:Zc(this._virtualLights)}))}catch{}}_beginContinuityCandidate(e,t,i="plan"){return this._booting&&!this._continuity.hasCompleteFrame?this._continuity.token:(this._continuityDataReady=t,this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._resumeSettling=!0,this._continuity.beginCandidate(e,i))}_continuityStageValid(){const e=this._stageEl;return!!e&&e.clientWidth>0&&e.clientHeight>0}_continuityAssetsReady(){if(!this._model.length)return!1;const e=this._model.length?this._spaceModel():null;return!e?.bg?.href||this._signer.isReady(this.hass,e.bg.href)}_initialSpaceSelection(e,t=this._loadOk){const i=this._fixedFloorState(e,t);return"valid"===i.kind?{id:i.id,source:"fixed"}:this._hasFixedFloor?{id:null,source:"none"}:function(e){const t=new Set(e.spaceIds.filter(e=>!!e));if(!t.size)return{id:null,source:"none"};const i=[["hash",!1===e.acceptHash?null:e.hashSpace],["current",e.preserveCurrent?e.currentSpace:null],["saved",e.savedSpace],["default",e.defaultSpace],["first",e.spaceIds[0]]];for(const[e,n]of i)if(n&&t.has(n))return{id:n,source:e};return{id:null,source:"none"}}({spaceIds:e.map(e=>e.id),hashSpace:this._hashSpace(),acceptHash:!this._hashApplied,currentSpace:this._space,preserveCurrent:this._hashApplied||this._navApplied||this._warmVpArmed,savedSpace:this._savedNav()?.space,defaultSpace:this._config?.default_floor})}_adoptInitialSpace(e,t=this._loadOk){const i=this._initialSpaceSelection(e,t);return i.id?(this._commitSpace(i.id,!0),"hash"===i.source&&(this._hashApplied=!0),"saved"===i.source&&(this._navApplied=!0),i):i}_candidateBackdrop(e,t=this._space){const i=Da(e),n=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==n.kind)return"";const r=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===r)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return Nl([this._cfgRev,this._cfgContentFingerprint||El(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||El(this._layout),this._space,this._mode,this._view,t,this._glowScreenBlend?"screen":"normal",this.hass?.themes?.darkMode??this.hass?.themes?.default_theme??""])}_settleContinuityFrame(){if(this._booting||!this._continuityStageValid())return;if(!this._continuity.hasCompleteFrame&&"steady"===this._continuity.state)return void(this._continuityAssetsReady()&&(this._renderSnapshotAt=Date.now(),this._continuity.markCompleteFrame(this._visualFrameFingerprint())));if(!this._continuityDataReady)return;if(!["holding","offline-stale","overlay-pending","overlay-visible","candidate-ready"].includes(this._continuity.state))return;const e=this._continuity.token;if(this._candidateDeviceSnapshot&&this._candidateDeviceSnapshot!==this._visibleDeviceSnapshot&&this._stagedDeviceSnapshotToken!==e)return this._stagedDeviceSnapshotToken=e,void this.requestUpdate();this._continuityPaintToken!==e&&(this._continuityPaintToken=e,this._continuity.candidateReady(e)&&this._continuity.commitAfterPaint(e,{updateComplete:()=>this.updateComplete,stageValid:()=>this.isConnected&&this._continuityStageValid(),assetsReady:()=>this._continuityAssetsReady(),frameFingerprint:()=>this._visualFrameFingerprint()}).then(t=>{t&&e===this._continuity.token?(this._resumeSettling=!1,this._renderSnapshotAt=Date.now(),this._candidateDeviceSnapshot&&(this._visibleDeviceSnapshot=this._candidateDeviceSnapshot),this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._warmSnapshot()):e===this._continuity.token&&(this._continuityPaintToken=-1,this._stagedDeviceSnapshotToken=-1,this._candidateDeviceSnapshot=null,this.requestUpdate())}))}_onBackdropLoaded(e,t){this._signer.markLoaded(this.hass,e,t),this._continuity.note("asset-ready"),this._continuityPaintToken=-1,"steady"!==this._continuity.state&&this.requestUpdate()}_renderRecoveryOverlay(){if(!this._continuity.overlayVisible&&"recovery-error"!==this._continuity.state)return V;const e="connection"===this._continuity.recoveryReason;return W`
    e.stopPropagation()} @click=${e=>e.stopPropagation()} @@ -4948,7 +4948,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef aria-label=${this._t("editor.loading_aria")}> ${this._t("editor.loading")} -
    `:V}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const e=this._serverCfg?.spaces||[];let t=e.length+":";for(const i of e){t+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const e of i.rooms||[]){const i=e.poly?.[0],n=e.poly?.[e.poly.length-1];t+=(e.poly?.length||0)+"."+(e.id||"")+"."+(e.open_to||[]).join("+")+"."+(e.area||"")+"."+JSON.stringify(e.settings||0)+"."+(e.x??"")+","+(e.y??"")+","+(e.w??"")+","+(e.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(n?n[0]+"/"+n[1]:"")+";"}}return t}get _model(){if(!this._serverCfg)return[];const e=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===e)return this._modelCache.model;const t=this._buildModel();return this._modelCache={key:e,model:t},t}_buildModel(){if(!this._serverCfg)return[];const e=this._renderCfg;return Ta(e).map((t,i)=>{const n=e.spaces[i]?.plan_url;return t.bg&&n?{...t,bg:{...t.bg,href:n}}:t})}_spaceModel(){return this._hasFixedFloor?Ll(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Ll(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!0,this._clearRoomFocus(!0);for(const e of this._pointers.keys())for(const t of this.renderRoot.querySelectorAll("*"))try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._drag=null,this._deviceDrag=null,this._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary?.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=cd({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!t.length&&e.settings?.filter_seeded)return;e.markers=e.markers||[];const i=[];for(const n of t){const t="h"+n.slice(n.indexOf(":")+1);e.markers.push({id:t,binding:n,hidden:!0}),i.push(n.slice(n.indexOf(":")+1))}const n={...e.settings||{},filter_seeded:!0};delete n.show_all,i.length&&Array.isArray(n.new_device_ids)&&(n.new_device_ids=n.new_device_ids.filter(e=>!i.includes(e))),e.settings=n,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const e=this._settings.icon_rules;if(!e||!Array.isArray(e)||!e.length)return;const t=JSON.stringify(e);return t!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=t,this._rulesCompiled=ye(e)),this._rulesCompiled}get _fillColors(){return Xi(this._settings)}get _excluded(){return fd(this._settings)}_setAreaLifecycleConfig(e){this._cfgEpochPreservedConfig=e,this._serverCfg=e}willUpdate(e){if(e.has("_serverCfg")){const e=this._cfgEpochPreservedConfig===this._serverCfg;this._cfgEpochPreservedConfig=null,e||this._cfgEpoch++}this._syncEmptySpaceState(),this._dangerConfirm&&(this._dangerConfirmMissingSpace()||"warm"===this._syncDangerConfirmLocaleGate())&&this._cancelDangerConfirm(),e.has("hass")&&this.hass&&(this._hassSequence++,this._renderSnapshotAt=Date.now(),this._continuity.note("hass-snapshot"),this._ensureHaRegistryAuthority(),this._planHassMemo=null,this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick()),this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._visualFrameFingerprint()),this._captureRenderDeviceSnapshot()}updated(){this._pruneDevicePressFeedback(),this._syncDayCycleClock(),this._warmSnapshot(),this._editorRuntime&&this._dtMeasure();const e=this._stageEl;e&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(e)),e&&this._booting&&!this._bootTimer&&this._bootWatch();const t=this.renderRoot.querySelector(".hdr");if(t&&e&&!this._roHdr){const i=()=>{const t=this.renderRoot.querySelector("ha-card");if(!t)return;const i=e.getBoundingClientRect().top-t.getBoundingClientRect().top,n=Math.min(Math.max(t.getBoundingClientRect().top,0),120),r=Math.round(i+n);r>=0&&r!==this._hdrH&&(this._hdrH=r),r>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:r,stageH:e.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(t),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(e&&!this._view&&this._refitView(),this._editorSecondary?.afterRender(),this._settleContinuityFrame(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const e=function(e){const t=e?.floors;if(!t||"object"!=typeof t)return[];const i=[];for(const e of Object.values(t))e&&e.floor_id&&i.push({id:e.floor_id,name:e.name||e.floor_id,level:e.level??null});return i.sort((e,t)=>{const i=e.level??1e9,n=t.level??1e9;return i!==n?i-n:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptConfigCapabilities(e){const t=e&&"object"==typeof e?e:{};this._haIntegrationVersion="string"==typeof t.integration_version?t.integration_version:this._haIntegrationVersion;const i=t.support_api;this._haSupportApi="number"==typeof i&&Number.isSafeInteger(i)?i:null,this._haDecorAssetsApi=1===t.decor_assets_api?1:null}async _syncDecorAssets(e){const t=++this._decorAssetSyncToken;if(1!==this._haDecorAssetsApi||!this.hass)return void(this._decorAssets=new Map);const i=await sm(this.hass,nm(e),this._cfgRev);t===this._decorAssetSyncToken&&(this._decorAssets=i,this._resign(),this.requestUpdate())}_adoptStructuralResponses(e,t,i){const n=e?.config,r=n&&Array.isArray(n.spaces)?n:null,o=Il(r),s=o!==(this._cfgContentFingerprint||Il(this._serverCfg));if(s&&(this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._pendingPhysicalWrites.clear(),this._clearRoomFocus(!0),this._cancelCameraTransition(!1),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=r,this._seedDecorStyle(this._serverCfg),this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=Yc(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);Zc(t)!==Zc(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let a=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},n=Il(e);a=n!==(this._layoutContentFingerprint||Il(this._layout)),a&&(this._cancelCameraTransition(!1),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._layout=e,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._adoptConfigCapabilities(e),this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),s&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),a&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:s,layoutChanged:a}}_resumePendingNavMode(){if(!this._pendingNavMode||!this._canEdit||this._config?.kiosk)return!1;const e=this._pendingNavMode;return this._pendingNavMode=null,this._setMode(e,!1),!0}async _loadFromServer(){this._loading=!0,this._loadTries++;const e=this._space,t=!!this._view;try{const[i,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),r=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Il(r)!==(this._cfgContentFingerprint||Il(this._serverCfg))||Il(n?.layout??{})!==(this._layoutContentFingerprint||Il(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(r)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0)}o&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),this._connectionWasLost=!1,this._serverStorage=!0,"boolean"==typeof i?.can_write&&(this._serverCanWrite=i.can_write),this._canOptimizeUndo=!(!i?.can_optimize_undo&&!n?.can_optimize_undo),this._adoptStructuralResponses(i,n),this._syncDecorAssets(r).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:t&&this._space===e||this._restoreZoom(),this._loadOk=!0,this.hass.callWS({type:"houseplan/trail/get"}).then(e=>{this._vacSrvTrails=e?.trails||{},this.requestUpdate()}).catch(()=>{}),this._ensureLiveSyncSubscriptions()}catch(e){if(this._serverCfg)this._scheduleLoadRetry(!0);else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(lf)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1,this._continuityDataReady=!0,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}}_ensureLiveSyncSubscriptions(){const e=this.hass?.connection;if(!e)return;if(e!==this._liveSyncConnection&&(this._unsubCfg?.(),this._unsubCfg=null,this._unsubLayout?.(),this._unsubLayout=null,this._unsubTrail?.(),this._unsubTrail=void 0,this._unsubVirtual?.(),this._unsubVirtual=null,this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=e),this._liveSyncAttempt)return;const t=this._liveSyncGeneration,i=[],n=(n,r,o,s)=>{n()||i.push(async()=>{const i=await e.subscribeEvents(s,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!n()?r(i):i?.()})};if(n(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),n(()=>this._unsubTrail,e=>{this._unsubTrail=e},"houseplan_trail_updated",async()=>{try{const e=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=e?.trails||{},this.requestUpdate()}catch{}}),n(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),n(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=eh(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const r=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=r,r.finally(()=>{this._liveSyncAttempt===r&&(this._liveSyncAttempt=null)})}async _reloadConfigOnly(e=!1,t){if(!e){if(void 0!==t&&t<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,t),400))}this._beginContinuityCandidate("config-reload",!1);try{const e=await this.hass.callWS({type:"houseplan/config/get"}),t=e?.config&&Array.isArray(e.config.spaces)?e.config:null;if(Il(t)!==(this._cfgContentFingerprint||Il(this._serverCfg))&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0);const i=this._space;this._adoptStructuralResponses(e),this._syncDecorAssets(t).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._space!==i&&this._restoreZoom(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(e){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(e)}))}finally{this._continuityDataReady=!0,this.requestUpdate()}}_scheduleLoadRetry(e=!1){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,!e&&this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRev=-1,this._haBindingCacheKey="",this._areaSnapshotCleanupCandidates.clear(),this._planHassMemo=null,this._haRegistryRelease=dh(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return ph(this.hass)}get _planHass(){const e=this._haRegistry,t=_h(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const n=fh(this.hass,e),r=vh(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:n,full:r},n}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRt.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+l_)>e?1:0)).join("|"),i=Zc(this._virtualLights);if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotLayout===this._layout&&this._capturedSnapshotConfigEpoch===this._cfgEpoch&&this._capturedSnapshotVirtual===i&&this._capturedSnapshotActivity===t)return;const n=this._planHass,r=new Map,o=new Map,s=new Set(["sun.sun"]);this._vacFit?.source&&s.add(this._vacFit.source);const a=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void s.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?a.add(n):"entity"===i&&s.add(n)};for(const e of this._model)for(const t of e.rooms)t.area&&l.add(t.area),c(t.settings?.temp_source),c(t.settings?.hum_source);for(const e of this._serverCfg?.spaces||[])for(const t of e.openings||[])for(const e of Kt(t))s.add(e);for(const e of this._serverCfg?.spaces||[])for(const t of e.decor||[])if("text"===t.kind){for(const e of String(t.text||"").matchAll(/\{([^{}\r\n]+)\}/g)){const t=Di(e[1]);t?.entity&&s.add(t.entity)}t.entity&&s.add(t.entity),o.set(`decor:${e.id}:${t.id}`,Oi(t.text,t,n,e=>!!n.entities?.[e]&&!!n.states?.[e]&&!ad(n,e,sd(this._markers))))}const h=Vh(n,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])r.set(pp(e.id,t),cp(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:h,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}));if(this._isVacDev(e)){const t=this._vacSource(e,n),i=t?Ql(n?.states?.[t]?.attributes):null,r=this._vacRt.get(e.id),s=fc(e.id,e.marker?.vacuum??null,e.space,t),a=yc({routes:s,observed:bc(s,[t],t=>this._vacObservedMapId(e,t,n)),spaceIds:new Set(this._model.map(e=>e.id))});o.set(`vacuum:${e.id}`,{source:t,telemetry:i,routes:s,resolution:a,mapId:i?this._vacMapId(e,i,n):null,runtime:r?{trail:r.trail,lastTs:r.lastTs,moving:r.moving,jump:r.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=up({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,positions:(u=this._model.length>0,p=this._devices,m=e=>this._livePos(e),u?new Map(p.map(e=>[e.id,m(e)])):new Map),facts:o,entityIds:s,deviceIds:a,areaIds:l});var u,p,m;this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotLayout=this._layout,this._capturedSnapshotActivity=t,this._capturedSnapshotConfigEpoch=this._cfgEpoch,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=d:(this._visibleDeviceSnapshot=d,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}get _renderPlanHass(){return this._renderDeviceSnapshot?.hass||this._planHass}get _renderDevices(){return this._renderDeviceSnapshot?.devices||this._devices}get _renderVacuumDevices(){return this._renderDeviceSnapshot?.vacuumDevices||this._devices}get _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return wh(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=ph(e);return{access:t.access,authoritative:t.authoritative,revision:t.revision,lastSuccess:t.lastSuccess,error:t.error}}(this.hass),t={active:0,ha_disabled:0,orphaned:0,unverified:0};for(const e of this._markers)e.removed||"virtual"===e.binding||t[this._bindingStatus(e.binding).kind]++;return{registry:{...e,lastSuccessAgeMs:e.lastSuccess?Math.max(0,Date.now()-e.lastSuccess):null},bindings:t}}_openBindingInHa(e){const[t,i]=e.split(":");if(i)if("device"!==t){if("entity"===t){const e=this._fullRegistryHass.entities?.[i];e?.device_id&&vf("/config/devices/device/"+encodeURIComponent(e.device_id))}}else vf("/config/devices/device/"+encodeURIComponent(i))}_bindingHasHaPage(e){const[t,i]=e.split(":");return!!i&&("device"===t||"entity"===t&&!!this._fullRegistryHass.entities?.[i]?.device_id)}_toggleMarkerDialogVisibility(){const e=this._markerDialog;if(!e)return;const t="ha"===e.bindingMode?this._bindingStatus(e.binding):null,i=e.hideFromPlan||"ha_disabled"===t?.kind;i&&"ha_disabled"===t?.kind?this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device")):i&&"unverified"===t?.kind?this._showToast(this._t("toast.ha_binding_unverified")):this._markerDialog={...e,hideFromPlan:!i}}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}_display(e){return this._signer.display(this.hass,e)}_referencedContentUrls(){const e=fn(this._serverCfg);for(const t of this._decorAssets.values())e.add(t.url);return e}_resign(){this._signer.resign(this.hass,this._referencedContentUrls())}_onLayoutEvent(e){e<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{e<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(e){const t=e?.rev;"number"==typeof t&&t>this._layoutRev&&(this._layoutRev=t)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;this._beginContinuityCandidate("layout-reload",!1);const e=new Map;for(const t of this._dirtyPos)this._layout[t]&&e.set(t,this._layout[t]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[t,i]of this._sentPos)e.set(t,i);try{const t=await this.hass.callWS({type:"houseplan/layout/get"}),i={...t?.layout||{}};for(const[t,n]of e)null===n?delete i[t]:i[t]=n;const n=Il(i);n!==Il(this._layout)&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear(),this._layout=i),this._layoutContentFingerprint=n,this._layoutRev=t?.rev??this._layoutRev,this._canOptimizeUndo=!!t?.can_optimize_undo,this._haIntegrationVersion="string"==typeof t?.integration_version?t.integration_version:this._haIntegrationVersion,this._undoKind=t?.undo_kind||null,this._cacheSnapshot(),this.requestUpdate()}catch{}finally{this._continuityDataReady=!0,this.requestUpdate()}}_persistLocalLayout(){this._layout=eo(Hr(this._layout)),localStorage.setItem(lf,JSON.stringify(this._layout))}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=_h(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+wu(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const n=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=dd({hass:e,registry:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:e=>this._t(e),iconRules:this._iconRules});const r=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+r.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const n of r)i.set(n,wh(e,n,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=bh(),i=Date.now();for(const[n,r]of e)"active"===r.kind?t[n]={kind:"active",ts:i}:"ha_disabled"===r.kind&&(t[n]={kind:"ha_disabled",reason:r.reason,ts:i});const n=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));oh=n;try{localStorage.setItem(rh,JSON.stringify(n))}catch{}}(i),this._haBindingCacheKey=o}let s=null;if(t.authoritative&&(s=this._resolveAreaRelocations(t),this._areaRelocationIds=new Set(s.relocateIds),this._areaRelocationIds.size)){this._cancelDeviceDrag();const e=this._areaRelocationIds;this._devicePositionHistory.removeWhere(({before:t,after:i})=>e.has(t.deviceId)||e.has(i.deviceId));const t=this._markerDialog;if(t?.devId&&!t.roomTouched&&this._areaRelocationIds.has(t.devId)){const e=this._devices.find(e=>e.id===t.devId);e&&(this._markerDialog={...t,room:e.marker?.room_id?`${e.space}#@${e.marker.room_id}`:e.space&&e.area?`${e.space}#${e.area}`:""})}}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),s&&this._syncAreaRelocations(s),this._syncActivityRuntime();const a=new Set(this._devices.filter(e=>!e.hidden).map(e=>e.id));for(const e of this._vacRt.keys())a.has(e)||this._vacRt.delete(e);if(this._infoCard){const e=this._devices.find(e=>e.id===this._infoCard.id);this._infoCard=e&&"ha_disabled"!==e.bindingStatus?.kind?e:null}const l=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==n.get(e.id));l&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._deviceDrag&&"ha_disabled"===this._devices.find(e=>e.id===this._deviceDrag.id)?.bindingStatus?.kind&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear()),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(ff(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_resolveAreaRelocations(e=this._haRegistry){const t=function(e){const t=Gp(e.snapshot),i=new Set(Object.values(t).map(e=>e.binding)),n=new Map;for(const[t,r]of e.previousCandidates||[])Vp(t)&&Number.isFinite(r)&&i.has(t)&&n.set(t,r);const r=new Set,o=new Set;for(const t of e.markers||[])t.removed||(Up(t.id)&&r.add(t.id),Vp(t.binding)&&o.add(t.binding));const s=new Set(Object.keys(e.registryDevices||{})),a=new Set(Object.keys(e.registryEntities||{})),l=new Set(Object.keys(e.liveStates||{})),c=new Map;for(const[e,i]of Object.entries(t)){const t=c.get(i.binding)||[];t.push(e),c.set(i.binding,t)}const h=new Set;let d=!1;for(const[t,i]of c){const[c,u]=Jp(t);if(o.has(t)||i.some(e=>r.has(e))||("device"===c?s.has(u):a.has(u)||l.has(u))){n.delete(t);continue}const p="device"===c?s.size>0:a.size>0;if(!e.authoritative||!p)continue;const m=n.get(t);if(void 0!==m){if(m!==e.revision)for(const e of i)h.add(e)}else n.set(t,e.revision),d=!0}return{removeIds:h,candidates:n,needsConfirmationRefresh:d}}({snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,revision:e.revision,registryDevices:e.devices,registryEntities:e.entities,liveStates:this.hass?.states,markers:this._markers,previousCandidates:this._areaSnapshotCleanupCandidates});return this._areaSnapshotCleanupCandidates=t.candidates,t.needsConfirmationRefresh&&this._canEdit&&uh(this.hass),Qp({devices:this._devices,model:this._model,layout:this._layout,snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,cleanupSnapshotIds:t.removeIds,coordinateScale:_f})}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const e=this._devices.filter(e=>!e.marker&&!e.virtual).map(e=>e.id).sort(),t=e.join(",");if(t===this._newSyncKey)return;this._newSyncKey=t;const i=this._settings,{fresh:n,known:r}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),n=e.filter(e=>!i.has(e));return{fresh:n,known:n.length?[...t,...n]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||n.length){const e=[...new Set([...i.new_device_ids||[],...n])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:r,new_device_ids:e}},this._saveConfig()}}_syncAreaRelocations(e){if(!(this._serverCfg&&this._norm&&this._canEdit&&this._haRegistry.authoritative))return;const t=e.decisions.filter(e=>e.updateSnapshot||e.removeSnapshot);if(!t.length)return;const i=Il({snapshot:Gp(this._settings.marker_area_snapshot),decisions:t});i!==this._areaRelocationSyncKey&&(this._areaRelocationSyncKey=i,this._areaRelocationWrite=this._areaRelocationWrite.catch(()=>{}).then(async()=>{if(!this._serverCfg||!this._haRegistry.authoritative)return;const e=this._resolveAreaRelocations();this._areaRelocationIds=new Set(e.relocateIds);const t=new Set,i=new Map;let n=!1;for(const r of e.decisions){if(!r.relocate)continue;const e=C_(this._layout,r.id);try{await this._persistDevicePlacement(r.id,null),t.add(r.id),e&&i.set(r.id,e),this._areaRelocationIds.delete(r.id)}catch(t){n=!0,this._layout=R_(this._layout,r.id,e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}}const r=this._settings,o=function(e,t,i=new Set){const n=Gp(e);for(const e of t.decisions)e.removeSnapshot?delete n[e.id]:e.updateSnapshot&&e.binding&&e.area&&(e.relocate&&!i.has(e.id)||(n[e.id]={binding:e.binding,area:e.area}));return n}(r.marker_area_snapshot,e,t),s=[...new Set([...Array.isArray(r.new_device_ids)?r.new_device_ids:[],...t])],a=Il(o)!==Il(Gp(r.marker_area_snapshot)),l=Il(s)!==Il(Array.isArray(r.new_device_ids)?r.new_device_ids:[]);if(a||l){const e=r.marker_area_snapshot,n=r.new_device_ids,a={...this._serverCfg,settings:{...r,marker_area_snapshot:o,...l?{new_device_ids:s}:{}}};this._setAreaLifecycleConfig(a);try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.cancel(),await this._writeConfig()}catch(r){const a=this._settings;if(Il(a.marker_area_snapshot)===Il(o)&&Il(a.new_device_ids)===Il(l?s:n)){const t={...a};void 0===e?delete t.marker_area_snapshot:t.marker_area_snapshot=e,void 0===n?delete t.new_device_ids:t.new_device_ids=n;const i={...this._serverCfg,settings:t};this._setAreaLifecycleConfig(i),this._cfgContentFingerprint=Il(this._serverCfg)}for(const e of t)this._areaRelocationIds.add(e);const c=new Set;for(const[e,t]of i)try{await this._persistDevicePlacement(e,t)}catch(t){c.add(e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}this._areaRelocationSyncKey="",this._regSignature="";if("conflict"===(r&&"object"==typeof r&&"code"in r?r.code:void 0)?(this._showToast(this._t("toast.conflict")),await this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(r)})),c.size&&this._serverCfg){const e=this._settings,t=[...new Set([...Array.isArray(e.new_device_ids)?e.new_device_ids:[],...c])],i={...this._serverCfg,settings:{...e,new_device_ids:t}};this._setAreaLifecycleConfig(i);try{await this._writeConfig()}catch(e){this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)}))}}}}n&&(this._areaRelocationSyncKey=""),this.requestUpdate()}))}get _newIds(){const e=this._settings.new_device_ids;return new Set([...Array.isArray(e)?e:[],...this._areaRelocationIds])}_ackNewDevice(e){if(!this._newIds.has(e)||!this._serverCfg)return;const t=this._settings;this._serverCfg={...this._serverCfg,settings:{...t,new_device_ids:(t.new_device_ids||[]).filter(t=>t!==e)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(e){if(!e)return null;const t=[];for(const i of this._renderDevices){if(i.area!==e||i.virtual)continue;const n=Yh(this._renderPlanHass,i.entities);null!=n&&t.push(n)}return fi(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}_defaultPositions(){const e={},t=this._config?.icon_size??2.5;for(const i of this._model){const n=t/100*tl(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const r=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!r.length)continue;const o=this._roomBounds(t),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(r.length*a/Math.max(l,1)))),h=Math.ceil(r.length/c),d=a/c,u=l/Math.max(h,1),p=r.map((e,t)=>({x:o.x+s+d*(t%c+.5),y:o.y+s+u*(Math.floor(t/c)+.5)}));yi(p,o,n,.5*s),r.forEach((t,i)=>e[t.id]=Ia(p[i]))}}return e}_pos(e){const t=this._renderDeviceSnapshot?.positions.get(e.id);return t?{x:t.x,y:t.y}:this._livePos(e)}_livePos(e){const t=this._spaceModelById(e.space),i=this._areaRelocationIds.has(e.id)?void 0:this._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*_f,y:i.y*_f}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Ia(el(t)):{x:500,y:500}}_devicePlacementForCanvas(e,t,i){if(!this._norm)return{x:Math.round(t),y:Math.round(i)};const n=this._gridPitch,r=Math.round(t/n)*n,o=Math.round(i/n)*n;return{s:e.space,x:Na(r/_f),y:Na(o/_f)}}_previewDevicePlacement(e,t){this._layout=R_(this._layout,e,t),this.requestUpdate()}_cancelDeviceDrag(){const e=this._deviceDrag;if(!e)return!1;this._deviceDrag=null;try{e.source?.hasPointerCapture?.(e.pointerId)&&e.source.releasePointerCapture(e.pointerId)}catch{}return this._previewDevicePlacement(e.id,e.before),!0}async _persistDevicePlacement(e,t){if(this._layout=R_(this._layout,e,t),this._serverStorage){let i=null,n=!1;try{let r;if(null===t)i=null,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/delete",device_id:e});else{const t=Qr(this._layout[e]);Il(t)!==Il(this._layout[e])&&(this._layout={...this._layout,[e]:t}),i=t,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t})}this._noteLayoutRev(r),this._layoutContentFingerprint=Il(this._layout),this._cacheSnapshot()}finally{n&&this._sentPos.get(e)===i&&this._sentPos.delete(e)}return}this._persistLocalLayout()}_devicePositionStateValid(e){const t=this._devices.find(t=>t.id===e.deviceId);return!!t&&t.space===e.spaceId&&"ha_disabled"!==t.bindingStatus?.kind&&!!this._spaceModelById(e.spaceId)}async _runDevicePositionHistory(e){if(this._cancelDeviceDrag()||this._devicePositionBusy)return;const t="undo"===e?this._devicePositionHistory.undo():this._devicePositionHistory.redo();if(!t)return;const i="undo"===e?t.before:t.after;if(!this._devicePositionStateValid(i))return this._devicePositionHistory.clear(),this._showToast(this._t("history.device_stale")),void this.requestUpdate();const n=C_(this._layout,i.deviceId);i.spaceId!==this._space&&(this._commitSpace(i.spaceId),this._restoreZoom()),this._devicePositionBusy=!0,this._previewDevicePlacement(i.deviceId,i.placement);try{await this._persistDevicePlacement(i.deviceId,i.placement),this._showToast(this._t("undo"===e?"history.undone":"history.redone",{name:t.name}))}catch(t){this._previewDevicePlacement(i.deviceId,n),"undo"===e?this._devicePositionHistory.redo():this._devicePositionHistory.undo(),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}finally{this._devicePositionBusy=!1,this.requestUpdate()}}_undoDevicePosition(){this._runDevicePositionHistory("undo")}_redoDevicePosition(){this._runDevicePositionHistory("redo")}_savePos(e,t,i){this._spaceModelById(e.space)&&(this._layout=R_(this._layout,e.id,this._devicePlacementForCanvas(e,t,i)),this._dirtyPos.add(e.id),this._persistLayout())}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const n=i?null:this._renderDeviceSnapshot?.presentations.get(pp(e.id,t));return n||cp(this._renderPlanHass,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,designPreview:i,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._renderDevices,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion})}_deviceVisual(e){return this._devicePresentation(e).visual}_stateClass(e,t=this._deviceVisual(e)){const i=this._devicePresentation(e);return i.effectiveHidden?"":("icon_ripple"!==i.display||!1===this._config?.live_states||"alarm"===t.status||t.activity,lp({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=Zh(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:Xh(this._renderPlanHass,e.entities)}_bindingEntities(e){if("virtual"===e||!e)return[];const t=this._bindingStatus(e);return"active"===t.kind?t.enabledEntityIds:t.allEntityIds}_bindingHasClimate(e){return this._bindingEntities(e).some(e=>e.startsWith("climate."))}_bindingHasAlarm(e){return this._bindingEntities(e).some(e=>{const t=this._planHass?.states?.[e]||this.hass?.states?.[e],i=this._fullRegistryHass.entities[e]||this.hass?.entities?.[e];return Rn(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_deviceBindingActive(e,t=!0){if(e.virtual||"virtual"===e.bindingKind)return!0;if(!e.bindingKind||!e.bindingRef)return!1;const i=this._bindingStatus(`${e.bindingKind}:${e.bindingRef}`);return"active"===i.kind||(t&&this._showToast(this._t("ha_disabled"===i.kind?"toast.ha_disabled_action":"toast.ha_binding_unverified")),!1)}_openMoreInfo(e){e?this._planEntityAvailable(e)?(this._nativeMoreInfoEntity=e,ff(this,"hass-more-info",{entityId:e})):this._showToast(this._t("toast.ha_disabled_action")):this._showToast(this._t("toast.no_entity"))}_interruptViewGesture(e,t){if(clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),void 0!==e)for(const i of[t,this._stageEl])try{i?.hasPointerCapture?.(e)&&i.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._roomPointer=null,this._doubleFit.clear()}_closeInfoCard(){this._interruptViewGesture(),this._holdFired=!1,this._infoCard=null}_ctxDevice(e,t){"view"===this._mode&&(e.preventDefault(),e.stopPropagation(),this._deviceBindingActive(t)&&(t.primary?this._openMoreInfo(t.primary):this._infoCard=t))}_clickDevice(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if(e.stopPropagation(),this._deviceDrag?.moved||this._suppressClick||this._holdFired)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const n=Nd(i.tapAction,i.primary?.split(".")[0]);if("none"===n)return;const r=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===n){const e=this._toggleIntent(i);if(!e)return;if(!jd(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=jd(e);if(!t)return;if("virtual-light"===t.kind)return this._startDevicePressFeedback(i.id),void this.hass.callWS({type:"houseplan/virtual_light/toggle",marker_id:t.markerId}).then(e=>{const t=eh(this._virtualLights,e);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}).catch(e=>this._showToast(this._t("toast.virtual_light_toggle_failed",{err:this._errText(e)})));const{command:n}=t;this._startDevicePressFeedback(i.id),this.hass.callService(n.domain,n.service,n.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},n=((o=e).targets.length?o.targets:o.skippedTargets).map(e=>e.name||e.entityId||e.ref).join(", ")||i.name;if(i.marker?.tap_confirm){const r=this._toggleConfirmationLines(e);if(!r.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:n}),lines:r,initialIntent:e,deviceId:i.id,exec:()=>{const n=this._devices.find(e=>e.id===i.id),r=n?this._toggleIntent(n):null;r&&!jd(r)&&this._showUnavailableToggleTargets(r)||(r&&Vd(e,r)?t(r):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===n)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===n){const e=i.marker?.tap_target||"",t=function(e){const t=String(e||"").split(".")[0];return"automation"===t?{domain:"automation",service:"trigger"}:"script"===t?{domain:"script",service:"turn_on"}:"scene"===t?{domain:"scene",service:"turn_on"}:null}(e),n=this.hass.states[e];if(!t||!n)return void this._showToast(this._t("toast.run_target_missing"));const o=n.attributes?.friendly_name||e;return void r(this._t("confirm.tap_run",{name:o}),()=>{this._deviceBindingActive(i)&&this._planEntityAvailable(e)&&(this._startDevicePressFeedback(i.id),this.hass.callService(t.domain,t.service,{entity_id:e}).then(()=>{this._stampActivity(i.id,"event",this._activitySourceKey(i)),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)}))))})}"more-info"===n&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||jd(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Ud.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const n of e.skippedTargets){if(!Ud.has(n.reason))continue;const e=String(n.name||n.entityId||n.ref||"").trim();e&&!i.has(e)&&(i.add(e),t.push(e))}return t}(e);return!!t.length&&(this._showToast(1===t.length?this._t("toast.toggle_target_unavailable",{name:t[0]}):this._t("toast.toggle_targets_unavailable",{names:t.join(", ")})),!0)}_keyDevice(e,t){"Enter"!==e.key&&" "!==e.key||"view"!==this._mode&&"devices"!==this._mode||(e.preventDefault(),this._clickDevice(e,t))}_startDevicePressFeedback(e){const t=[...this.renderRoot.querySelectorAll(".dev[data-id]")].find(t=>t.dataset.id===e),i=t?.querySelector(".device-shell");if(!i||"function"!=typeof i.animate)return;const n=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),r=Number.isFinite(n)?Math.max(.95,Math.min(1,n)):1;this._devicePressAnimations.get(e)?.cancel();const o=this._reducedMotion?[{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"},{outlineColor:"var(--hp-accent, #3ea6ff)",outlineStyle:"solid",outlineWidth:"2px",outlineOffset:"2px",offset:.5},{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"}]:[{scale:String(r)},{scale:"0.95",offset:.5},{scale:"1"}],s=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,s);const a=()=>{this._devicePressAnimations.get(e)===s&&this._devicePressAnimations.delete(e)};s.addEventListener("finish",a,{once:!0}),s.addEventListener("cancel",a,{once:!0})}_cancelDevicePressFeedback(){for(const e of this._devicePressAnimations.values())e.cancel();this._devicePressAnimations.clear()}_pruneDevicePressFeedback(){if(!this._devicePressAnimations.size)return;const e=new Set([...this.renderRoot.querySelectorAll(".dev[data-id]")].map(e=>e.dataset.id||""));for(const[t,i]of this._devicePressAnimations)e.has(t)||(i.cancel(),this._devicePressAnimations.delete(t))}_t(e,t){return ku(wu(this.hass,this._config?.language),e,t)}get _colorPickerLabels(){return{title:this._t("color_picker.title"),hue:this._t("color_picker.hue"),saturation:this._t("color_picker.saturation"),value:this._t("color_picker.value"),hex:this._t("color_picker.hex"),invalidHex:this._t("color_picker.invalid_hex")}}_help(e){return this._editorRuntimeOrThrow()._help(e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(e){const t=[];for(const i of this._devices){if(i.space!==e.id||i.hidden)continue;const n=this._pos(i);t.push({minX:n.x,minY:n.y,maxX:n.x,maxY:n.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,n=Math.cos(i)*e.rlen/2,r=Math.sin(i)*e.rlen/2,o=qa([[e.rx-n,e.ry-r],[e.rx+n,e.ry+r]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){if("image"===e.kind&&!this._decorAssets.has(e.asset_id)&&"decor"!==this._mode)continue;const n=qa("line"===e.kind?[[e.x1*_f,e.y1*i],[e.x2*_f,e.y2*i]]:"text"===e.kind?[[e.x*_f,e.y*i]]:mr({x:e.x*_f,y:e.y*i,w:e.w*_f,h:e.h*i,angle:e.angle}));n&&t.push(n)}for(const i of this._physicalBodiesR(e)){const e=qa(i);e&&t.push(e)}}return Ga(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:_f,h:_f};return{rect:e,all:e,outliers:0}}const t=this._frame,i="view"!==this._mode;if(t&&t.id===e.id&&this._bdDrag)return t;if(t&&t.id===e.id&&t.model===e&&t.layout===this._layout&&t.devs===this._devices&&t.far===this._showFar&&t.grow===i)return t;const n=Za(this._contentItems(e));let r=n.all||Qa(e),o=this._showFar?r:n.core||Qa(e);return t&&t.id===e.id&&i&&t.grow&&(o=gf(t.rect,o),r=gf(t.all,r)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:r,outliers:n.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),n=this._openingsR.map((e,t)=>({id:String(e.id||t),sourceIndex:t,type:e.type,x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen>0?e.rlen:900,flipH:!!e.flip_h,flipV:!!e.flip_v})),r=ya(J_,this._cellCm),o=ya(10,this._cellCm),s=`${e.id}|${function(e){let t=2166136261;const i=e=>{if("number"!=typeof e)if("string"!=typeof e)if("boolean"!=typeof e)if(Array.isArray(e)){t=Ng(t,e.length);for(const t of e)i(t)}else{if(e&&"object"==typeof e){const t=e;for(const e of Object.keys(t).sort())i(e),i(t[e]);return}t=Ng(t,-1)}else t=Ng(t,e?1:0);else for(const i of e)t=Ng(t,i.codePointAt(0)||0);else t=Ng(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:n,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:Z_,wallHeight:r,floorEdgeHeight:o,algorithm:3})}`;return{key:s,build:()=>{const o=oa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,s=this._roomWallOpeningInputs(this._openingsR,e),a=t.length||o.length?Ps(e.rooms,t,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,_f,o):null;if(a&&("failed-core"===a.status||"not-applicable"===a.status))throw new Error("wall boolean geometry failed");const l=a?.paperGeom??Ts(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f);if(!l)throw new Error("floor boolean geometry failed");const c=a?.openingIndex||Ns(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f),h=n.map(e=>{const i=this._openingsR[e.sourceIndex],n="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?tu(i.partitionHost,n):t.length||"gate"===e.type?qs(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:n}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(Hg([e.x,e.y,e.angle,e.length,e.face.ox,e.face.oy,t])&&e.length>0&&t>0))throw new Error("invalid isometric opening input");const i=e.length/2;let n;if("passage"===e.type)n=[];else if("gate"===e.type){const r=10*e.face.side;n=[Bg(e,0,[-i,0],[i,0],r,0,.88*t),Bg(e,1,[i,0],[-i,0],-r,0,.88*t)]}else n="window"===e.type?[Bg(e,0,[-i,0],[i,0],-90,.27*t,.78*t),Bg(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[Bg(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(n.map(e=>Object.freeze(e)))}}({...e,face:o},r)});return{walls:a?.components.flatMap(e=>e.geom)||[],floor:l,openings:Object.freeze(h)}}}}_isoSceneKey(){if(!this._labsIso||"view"!==this._mode)return null;try{return this._isoSource()?.key??null}catch{return`${this._space}|invalid`}}_isoScene(){const e=this._isoSource();if(!e)return null;const t=this._isoGeometryCache.get(e.key);if(t)return{key:e.key,...t};const i=this._frameOf().rect,n=e.build(),r=ya(J_,this._cellCm),o=ya(10,this._cellCm),s=function(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)for(const e of o.leaves){const o=Math.hypot(e.closedVector[0],e.closedVector[1]);t=Math.min(t,e.hinge[0]-o),i=Math.min(i,e.hinge[1]-o),n=Math.max(n,e.hinge[0]+o),r=Math.max(r,e.hinge[1]+o)}return Hg([t,i,n,r])?{x:t,y:i,w:n-t,h:r-i}:null}(n.openings),a=ng({rect:s?gf(i,s):i,wallHeight:r,openingHeight:r,floorDepth:o}),l=function(e,t=Z_,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const n=[],r=[],o=[];let s=0;for(let a=0;a<(e||[]).length;a++){const l=e[a];for(let e=0;e<(l||[]).length;e++){const c=Fg(l[e],e>0);if(!(c.length<3||Math.abs(Pg(c))<1e-9)){n.push(Og(c,t,i)),s+=c.length;for(let n=0;ne.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:n.join(" "),sides:r,contactPath:o.join(" "),edgeCount:s}}(n.walls,Z_,r),c={geometry:l,floor:Eg(n.floor,o),openings:n.openings,frame:a};return gg(this._isoGeometryCache,e.key,c,8),{key:e.key,...c}}_effectiveProjection(){if("iso"!==this._desiredProjection||!this._model.length)return"flat";const e=this._isoSceneKey()||`${this._space}|invalid`;if(this._isoFallback.has(e))return"flat";try{const e=this._isoScene();return e?(t="iso",i=e.key,n=this._isoFallback,"iso"===t&&i&&!n.has(i)?"iso":"flat"):"flat"}catch(t){if(!this._isoFallback.has(e)){this._isoFallback.add(e);const i=e.split("|");console.warn(`HOUSEPLAN ISO FALLBACK: #89, space ${this._space}, fingerprint ${i[i.length-1]}, ${t instanceof Error?t.message:"renderer error"}`)}return"flat"}var t,i,n}_scenePoint(e){return"iso"===this._renderProjection?eg(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=tg([e.x,e.y]),i=tg([e.x+e.w,e.y+e.h]);return{x:t[0],y:t[1],w:i[0]-t[0],h:i[1]-t[1]}}_baseVb(){if("iso"===this._effectiveProjection()){if(!this._spaceDisplayForRender().showBorders){const e=ng({rect:this._frameOf().rect,wallHeight:ya(J_,this._cellCm)});return[e.x,e.y,e.w,e.h]}const e=this._isoScene()?.frame??this._frameOf().rect;return[e.x,e.y,e.w,e.h]}const e=this._frameOf().rect;return[e.x,e.y,e.w,e.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom()}_clearRoomFocus(e=!1){this._doubleFit.clear(),this._roomFocus=null,e&&(this._roomPointer=null)}_roomFitBounds(e,t){const i=Ht(e);if(!i||!e.id)return null;const n=this._spaceWalls,r=this._openCuts(),o=this._wallUnionGeometry(),s=n.length&&this._innerRoomContour(t,e.id,r,o?.roomGeom,o?.multiWallNodes)||i,a=this._spaceDisplayForRender();let l=[];if(a.showBorders&&n.length){const i=hs(t.rooms,e.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,_f);i&&(l=Es(i.poly,i.offsets,o?.multiWallNodes)||i.poly)}const c=this._effectiveProjection();return function(e){const t=e.floor,i=e.wall||[],n=e.padding??0;if(!t.length||!t.every(cg)||!i.every(cg)||!Number.isFinite(n)||n<0)return null;if("flat"===e.projection)return dg([...t,...i],n);const r=e.wallHeight??0,o=e.floorDepth??0;if(!Number.isFinite(r)||r<0||!Number.isFinite(o)||o<0)return null;try{const e=[];for(const i of t){const t=[i[0],i[1]];e.push(eg(t,0)),o>0&&e.push(eg(t,-o))}for(const t of i){const i=[t[0],t[1]];e.push(eg(i,0)),r>0&&e.push(eg(i,r))}return dg(e,n)}catch{return null}}({floor:s,wall:l,projection:c,wallHeight:"iso"===c&&a.showBorders?ya(J_,this._cellCm):0,floorDepth:"iso"===c&&a.showBorders?ya(10,this._cellCm):0,padding:a.showBorders?ya(1.25,this._cellCm):0})}_fitRoom(e,t=!0){if("view"!==this._mode)return!1;this._doubleFit.clear();const i=this._spaceModel(),n=i?.rooms.find(t=>t.id===e);if(!i||!n)return this._clearRoomFocus(),!1;const r=this._roomFitBounds(n,i);if(!r)return this._clearRoomFocus(),!1;this._roomFocus={spaceId:i.id,roomId:e};const o=this._stageEl;if(!o||o.clientWidth<=0||o.clientHeight<=0)return!0;const s=vi(this._baseVb(),o.clientWidth/o.clientHeight),a=function(e){const t=e.safeFraction??.8,{bounds:i,baseFit:n,stageWidth:r,stageHeight:o,minZoom:s,maxZoom:a}=e;if(!hg(i)||!hg(n)||!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||!Number.isFinite(s)||s<=0||!Number.isFinite(a)||a1)return null;const l=r/o,c=Math.max(i.w/t,i.h/t*l);if(!Number.isFinite(c)||c<=0)return null;const h=Math.min(a,Math.max(s,n.w/c)),d=n.w/h,u=d/l;return{zoom:h,viewBox:{x:i.x+i.w/2-d/2,y:i.y+i.h/2-u/2,w:d,h:u}}}({bounds:r,baseFit:s,stageWidth:o.clientWidth,stageHeight:o.clientHeight,minZoom:kf.ZOOM_MIN,maxZoom:kf.ZOOM_MAX});if(!a)return this._clearRoomFocus(),!1;const l=this._cameraState(),c=function(e,t,i,n){const r=[e,t,i,n],o=Math.min(...r.map(e=>e.x)),s=Math.min(...r.map(e=>e.y));return{x:o,y:s,w:Math.max(...r.map(e=>e.x+e.w))-o,h:Math.max(...r.map(e=>e.y+e.h))-s}}(s,l.viewBox,a.viewBox,r);return a.viewBox=this._clampView(a.viewBox,c),t?(this._prepareCameraCommand(),this._startCameraTransition(a,c,"room",220),!0):(this._cancelCameraTransition(!1),G_(l,a)||(this._zoom=a.zoom,this._view={...a.viewBox},this.requestUpdate()),!0)}_fitAll(e="fit"){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom(e)}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
    +
    `:V}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const e=this._serverCfg?.spaces||[];let t=e.length+":";for(const i of e){t+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const e of i.rooms||[]){const i=e.poly?.[0],n=e.poly?.[e.poly.length-1];t+=(e.poly?.length||0)+"."+(e.id||"")+"."+(e.open_to||[]).join("+")+"."+(e.area||"")+"."+JSON.stringify(e.settings||0)+"."+(e.x??"")+","+(e.y??"")+","+(e.w??"")+","+(e.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(n?n[0]+"/"+n[1]:"")+";"}}return t}get _model(){if(!this._serverCfg)return[];const e=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===e)return this._modelCache.model;const t=this._buildModel();return this._modelCache={key:e,model:t},t}_buildModel(){if(!this._serverCfg)return[];const e=this._renderCfg;return Da(e).map((t,i)=>{const n=e.spaces[i]?.plan_url;return t.bg&&n?{...t,bg:{...t.bg,href:n}}:t})}_spaceModel(){return this._hasFixedFloor?Bl(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Bl(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!0,this._clearRoomFocus(!0);for(const e of this._pointers.keys())for(const t of this.renderRoot.querySelectorAll("*"))try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._drag=null,this._deviceDrag=null,this._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._physicalDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._roomDialog=!1,"edit"===this._spaceDialog?.mode&&(this._spaceDialog=null),this._editorSecondary?.closeForNavigation(),this._saveConfigDebounced.cancel(),this._frame=null,this._planSnapGeometryCache=null,this._hiddenWallDiagnosticCache=null,this._decorSnapCache=null,this._commitSpace("",!0)}}else this._emptySpaceStateActive=!1}get _areaToSpace(){const e={};for(const t of this._model)for(const i of t.rooms)i.area&&(e[i.area]={space:t.id,room:i});return e}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._showHidden||!this._settings.filter_seeded&&!!this._settings.show_all}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=dd({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!t.length&&e.settings?.filter_seeded)return;e.markers=e.markers||[];const i=[];for(const n of t){const t="h"+n.slice(n.indexOf(":")+1);e.markers.push({id:t,binding:n,hidden:!0}),i.push(n.slice(n.indexOf(":")+1))}const n={...e.settings||{},filter_seeded:!0};delete n.show_all,i.length&&Array.isArray(n.new_device_ids)&&(n.new_device_ids=n.new_device_ids.filter(e=>!i.includes(e))),e.settings=n,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const e=this._settings.icon_rules;if(!e||!Array.isArray(e)||!e.length)return;const t=JSON.stringify(e);return t!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=t,this._rulesCompiled=ye(e)),this._rulesCompiled}get _fillColors(){return Xi(this._settings)}get _excluded(){return yd(this._settings)}_setAreaLifecycleConfig(e){this._cfgEpochPreservedConfig=e,this._serverCfg=e}willUpdate(e){if(e.has("_serverCfg")){const e=this._cfgEpochPreservedConfig===this._serverCfg;this._cfgEpochPreservedConfig=null,e||this._cfgEpoch++,this._renderLife.invalidate()}if(this._syncEmptySpaceState(),this._dangerConfirm&&(this._dangerConfirmMissingSpace()||"warm"===this._syncDangerConfirmLocaleGate())&&this._cancelDangerConfirm(),e.has("hass")&&this.hass){const t=this._visibleDeviceSnapshot||this._candidateDeviceSnapshot;this._renderLife.observe(e.get("hass"),this.hass,t?{entityIds:t.entityIds}:null,()=>A_(this))}this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._continuity.refreshCompleteFrame(this._visualFrameFingerprint()),this._captureRenderDeviceSnapshot()}updated(){this._liveRt?.commit(),this._editorRuntime?._commitLiveEditor(),this._pruneDevicePressFeedback(),this._syncDayCycleClock(),this._warmSnapshot(),this._editorRuntime&&this._dtMeasure();const e=this._stageEl;e&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(e)),e&&this._booting&&!this._bootTimer&&this._bootWatch();const t=this.renderRoot.querySelector(".hdr");if(t&&e&&!this._roHdr){const i=()=>{const t=this.renderRoot.querySelector("ha-card");if(!t)return;const i=e.getBoundingClientRect().top-t.getBoundingClientRect().top,n=Math.min(Math.max(t.getBoundingClientRect().top,0),120),r=Math.round(i+n);r>=0&&r!==this._hdrH&&(this._hdrH=r),r>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:r,stageH:e.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(t),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(e&&!this._view&&this._refitView(),this._editorSecondary?.afterRender(),this._settleContinuityFrame(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const e=function(e){const t=e?.floors;if(!t||"object"!=typeof t)return[];const i=[];for(const e of Object.values(t))e&&e.floor_id&&i.push({id:e.floor_id,name:e.name||e.floor_id,level:e.level??null});return i.sort((e,t)=>{const i=e.level??1e9,n=t.level??1e9;return i!==n?i-n:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptConfigCapabilities(e){const t=e&&"object"==typeof e?e:{};this._haIntegrationVersion="string"==typeof t.integration_version?t.integration_version:this._haIntegrationVersion;const i=t.support_api;this._haSupportApi="number"==typeof i&&Number.isSafeInteger(i)?i:null,this._haDecorAssetsApi=1===t.decor_assets_api?1:null}async _syncDecorAssets(e){const t=++this._decorAssetSyncToken;if(1!==this._haDecorAssetsApi||!this.hass)return void(this._decorAssets=new Map);const i=await lm(this.hass,om(e),this._cfgRev);t===this._decorAssetSyncToken&&(this._decorAssets=i,this._resign(),this.requestUpdate())}_adoptStructuralResponses(e,t,i){const n=e?.config,r=n&&Array.isArray(n.spaces)?n:null,o=El(r),s=o!==(this._cfgContentFingerprint||El(this._serverCfg));if(s&&(this._geometryHistory.clear(),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._pendingPhysicalWrites.clear(),this._clearRoomFocus(!0),this._cancelCameraTransition(!1),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=r,this._seedDecorStyle(this._serverCfg),this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=Xc(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);Jc(t)!==Jc(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let a=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},n=El(e);a=n!==(this._layoutContentFingerprint||El(this._layout)),a&&(this._cancelCameraTransition(!1),this._devicePositionHistory.clear(),this._cancelDeviceDrag(),this._layout=e,this._layoutContentFingerprint=n),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._adoptConfigCapabilities(e),this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),s&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),a&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:s,layoutChanged:a}}_resumePendingNavMode(){if(!this._pendingNavMode||!this._canEdit||this._config?.kiosk)return!1;const e=this._pendingNavMode;return this._pendingNavMode=null,this._setMode(e,!1),!0}async _loadFromServer(){this._loading=!0,this._loadTries++;const e=this._space,t=!!this._view;try{const[i,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),r=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=El(r)!==(this._cfgContentFingerprint||El(this._serverCfg))||El(n?.layout??{})!==(this._layoutContentFingerprint||El(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(r)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0)}o&&this._continuity.hasCompleteFrame&&"steady"===this._continuity.state&&this._beginContinuityCandidate("structural-response",!0),this._connectionWasLost=!1,this._serverStorage=!0,"boolean"==typeof i?.can_write&&(this._serverCanWrite=i.can_write),this._canOptimizeUndo=!(!i?.can_optimize_undo&&!n?.can_optimize_undo),this._adoptStructuralResponses(i,n),this._syncDecorAssets(r).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:t&&this._space===e||this._restoreZoom(),this._loadOk=!0,this.hass.callWS({type:"houseplan/trail/get"}).then(e=>{this._vacSrvTrails=e?.trails||{},this.requestUpdate()}).catch(()=>{}),this._ensureLiveSyncSubscriptions()}catch(e){if(this._serverCfg)this._scheduleLoadRetry(!0);else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(uf)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1,this._continuityDataReady=!0,this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}}_ensureLiveSyncSubscriptions(){const e=this.hass?.connection;if(!e)return;if(e!==this._liveSyncConnection&&(this._unsubCfg?.(),this._unsubCfg=null,this._unsubLayout?.(),this._unsubLayout=null,this._unsubTrail?.(),this._unsubTrail=void 0,this._unsubVirtual?.(),this._unsubVirtual=null,this._liveSyncGeneration++,this._liveSyncAttempt=null,this._liveSyncConnection=e),this._liveSyncAttempt)return;const t=this._liveSyncGeneration,i=[],n=(n,r,o,s)=>{n()||i.push(async()=>{const i=await e.subscribeEvents(s,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!n()?r(i):i?.()})};if(n(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),n(()=>this._unsubTrail,e=>{this._unsubTrail=e},"houseplan_trail_updated",async()=>{try{const e=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=e?.trails||{},this.requestUpdate()}catch{}}),n(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),n(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=th(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const r=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=r,r.finally(()=>{this._liveSyncAttempt===r&&(this._liveSyncAttempt=null)})}async _reloadConfigOnly(e=!1,t){if(!e){if(void 0!==t&&t<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,t),400))}this._beginContinuityCandidate("config-reload",!1);try{const e=await this.hass.callWS({type:"houseplan/config/get"}),t=e?.config&&Array.isArray(e.config.spaces)?e.config:null;if(El(t)!==(this._cfgContentFingerprint||El(this._serverCfg))&&!await this._signer.prepareImage(this.hass,this._candidateBackdrop(t)))return this._continuity.note("asset-failed"),void this._scheduleLoadRetry(!0);const i=this._space;this._adoptStructuralResponses(e),this._syncDecorAssets(t).catch(()=>{}),this._adoptInitialSpace(this._model,!0),this._resumePendingNavMode(),this._cacheSnapshot(),this._space!==i&&this._restoreZoom(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(e){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(e)}))}finally{this._continuityDataReady=!0,this.requestUpdate()}}_scheduleLoadRetry(e=!1){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,!e&&this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}async _ensureLiveRuntime(){if(this._liveRt)return;const e=await import("./live-interaction-runtime-C2JvTP_O.js");this._liveRt=new e.LiveRuntime(this),this.isConnected&&this.requestUpdate()}_liveVp(e=!1){this._liveRt?this._liveRt.viewport(e):this.requestUpdate()}_syncLiveHover(){this._liveRt?this._liveRt.hover():this.requestUpdate()}_flushHa(){this._liveRt?.take()&&(this._terminalFrame=2,super.requestUpdate())}_ensureHaRegistryAuthority(){const e=this.hass?.connection||null;e&&e!==this._haRegistryConnection&&(this._haRegistryRelease?.(),this._haRegistryConnection=e,this._haRegistryRev=-1,this._haBindingCacheKey="",this._renderLife.invalidate(),this._areaSnapshotCleanupCandidates.clear(),this._planHassMemo=null,this._haRegistryRelease=uh(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return mh(this.hass)}get _planHass(){const e=this._haRegistry,t=gh(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const n=vh(this.hass,e),r=yh(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:n,full:r},n}_captureRenderDeviceSnapshot(){if(!this.hass)return;const e=Date.now(),t=[...this._activityRt.entries()].map(([t,i])=>`${t}:${i.gen}:${i.flashTs}:`+(i.flashKind&&(i.expiresAt||i.flashTs+h_)>e?1:0)).join("|"),i=Jc(this._virtualLights);if(this._capturedSnapshotSequence===this._hassSequence&&this._capturedSnapshotDevices===this._devices&&this._capturedSnapshotLayout===this._layout&&this._capturedSnapshotConfigEpoch===this._cfgEpoch&&this._capturedSnapshotVirtual===i&&this._capturedSnapshotActivity===t)return;const n=this._planHass,r=new Map,o=new Map,s=new Set(["sun.sun"]);this._vacFit?.source&&s.add(this._vacFit.source);const a=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void s.add(e);const i=e.slice(0,t),n=e.slice(t+1);"device"===i?a.add(n):"entity"===i&&s.add(n)};for(const e of this._model)for(const t of e.rooms)t.area&&l.add(t.area),c(t.settings?.temp_source),c(t.settings?.hum_source);for(const e of this._serverCfg?.spaces||[])for(const t of e.openings||[])for(const e of Kt(t))s.add(e);for(const e of this._serverCfg?.spaces||[])for(const t of e.decor||[])if("text"===t.kind){for(const e of String(t.text||"").matchAll(/\{([^{}\r\n]+)\}/g)){const t=Di(e[1]);t?.entity&&s.add(t.entity)}t.entity&&s.add(t.entity),o.set(`decor:${e.id}:${t.id}`,Oi(t.text,t,n,e=>!!n.entities?.[e]&&!!n.states?.[e]&&!cd(n,e,ld(this._markers))))}const h=Kh(n,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])r.set(_p(e.id,t),dp(n,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._devices,lightSources:h,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}));if(this._isVacDev(e)){const t=this._vacSource(e,n),i=t?ec(n?.states?.[t]?.attributes):null,r=this._vacRt.get(e.id),s=vc(e.id,e.marker?.vacuum??null,e.space,t),a=bc({routes:s,observed:wc(s,[t],t=>this._vacObservedMapId(e,t,n)),spaceIds:new Set(this._model.map(e=>e.id))});o.set(`vacuum:${e.id}`,{source:t,telemetry:i,routes:s,resolution:a,mapId:i?this._vacMapId(e,i,n):null,runtime:r?{trail:r.trail,lastTs:r.lastTs,moving:r.moving,jump:r.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=mp({sourceSequence:this._hassSequence,hass:n,devices:this._devices,presentations:r,positions:(u=this._model.length>0,p=this._devices,m=e=>this._livePos(e),u?new Map(p.map(e=>[e.id,m(e)])):new Map),facts:o,entityIds:s,deviceIds:a,areaIds:l});var u,p,m;this._capturedSnapshotSequence=this._hassSequence,this._capturedSnapshotDevices=this._devices,this._capturedSnapshotLayout=this._layout,this._capturedSnapshotActivity=t,this._capturedSnapshotConfigEpoch=this._cfgEpoch,this._capturedSnapshotVirtual=i,this._visibleDeviceSnapshot&&"steady"!==this._continuity.state?this._candidateDeviceSnapshot=d:(this._visibleDeviceSnapshot=d,this._candidateDeviceSnapshot=null)}get _renderDeviceSnapshot(){return this._stagedDeviceSnapshotToken===this._continuity.token?this._candidateDeviceSnapshot||this._visibleDeviceSnapshot:this._visibleDeviceSnapshot||this._candidateDeviceSnapshot}get _renderPlanHass(){return this._renderDeviceSnapshot?.hass||this._planHass}get _renderDevices(){return this._renderDeviceSnapshot?.devices||this._devices}get _renderVacuumDevices(){return this._renderDeviceSnapshot?.vacuumDevices||this._devices}get _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return kh(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const{registry:e,bindings:t}=this._renderLife.diagnostics(this.hass,this._markers,e=>this._bindingStatus(e));return{registry:{...e,lastSuccessAgeMs:e.lastSuccess?Math.max(0,Date.now()-e.lastSuccess):null},bindings:t}}_openBindingInHa(e){const[t,i]=e.split(":");if(i)if("device"!==t){if("entity"===t){const e=this._fullRegistryHass.entities?.[i];e?.device_id&&kf("/config/devices/device/"+encodeURIComponent(e.device_id))}}else kf("/config/devices/device/"+encodeURIComponent(i))}_bindingHasHaPage(e){const[t,i]=e.split(":");return!!i&&("device"===t||"entity"===t&&!!this._fullRegistryHass.entities?.[i]?.device_id)}_toggleMarkerDialogVisibility(){const e=this._markerDialog;if(!e)return;const t="ha"===e.bindingMode?this._bindingStatus(e.binding):null,i=e.hideFromPlan||"ha_disabled"===t?.kind;i&&"ha_disabled"===t?.kind?this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device")):i&&"unverified"===t?.kind?this._showToast(this._t("toast.ha_binding_unverified")):this._markerDialog={...e,hideFromPlan:!i}}_hookConnection(){const e=this.hass?.connection;e&&e!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked?.removeEventListener?.("disconnected",this._onConnLost),this._connHooked?.removeEventListener?.("reconnect-error",this._onConnLost),e.addEventListener?.("ready",this._onConnReady),e.addEventListener?.("disconnected",this._onConnLost),e.addEventListener?.("reconnect-error",this._onConnLost),this._connHooked=e)}_display(e){return this._signer.display(this.hass,e)}_referencedContentUrls(){const e=fn(this._serverCfg);for(const t of this._decorAssets.values())e.add(t.url);return e}_resign(){this._signer.resign(this.hass,this._referencedContentUrls())}_onLayoutEvent(e){e<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{e<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(e){const t=e?.rev;"number"==typeof t&&t>this._layoutRev&&(this._layoutRev=t)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;this._beginContinuityCandidate("layout-reload",!1);const e=new Map;for(const t of this._dirtyPos)this._layout[t]&&e.set(t,this._layout[t]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[t,i]of this._sentPos)e.set(t,i);try{const t=await this.hass.callWS({type:"houseplan/layout/get"}),i={...t?.layout||{}};for(const[t,n]of e)null===n?delete i[t]:i[t]=n;const n=El(i);n!==El(this._layout)&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear(),this._layout=i),this._layoutContentFingerprint=n,this._layoutRev=t?.rev??this._layoutRev,this._canOptimizeUndo=!!t?.can_optimize_undo,this._haIntegrationVersion="string"==typeof t?.integration_version?t.integration_version:this._haIntegrationVersion,this._undoKind=t?.undo_kind||null,this._cacheSnapshot(),this.requestUpdate()}catch{}finally{this._continuityDataReady=!0,this.requestUpdate()}}_persistLocalLayout(){this._layout=eo(Hr(this._layout)),localStorage.setItem(uf,JSON.stringify(this._layout))}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=gh(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+xu(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const n=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=pd({hass:e,registry:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:e=>this._t(e),iconRules:this._iconRules});const r=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+r.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const n of r)i.set(n,kh(e,n,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=wh(),i=Date.now();for(const[n,r]of e)"active"===r.kind?t[n]={kind:"active",ts:i}:"ha_disabled"===r.kind&&(t[n]={kind:"ha_disabled",reason:r.reason,ts:i});const n=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));sh=n;try{localStorage.setItem(oh,JSON.stringify(n))}catch{}}(i),this._haBindingCacheKey=o}let s=null;if(t.authoritative&&(s=this._resolveAreaRelocations(t),this._areaRelocationIds=new Set(s.relocateIds),this._areaRelocationIds.size)){this._cancelDeviceDrag();const e=this._areaRelocationIds;this._devicePositionHistory.removeWhere(({before:t,after:i})=>e.has(t.deviceId)||e.has(i.deviceId));const t=this._markerDialog;if(t?.devId&&!t.roomTouched&&this._areaRelocationIds.has(t.devId)){const e=this._devices.find(e=>e.id===t.devId);e&&(this._markerDialog={...t,room:e.marker?.room_id?`${e.space}#@${e.marker.room_id}`:e.space&&e.area?`${e.space}#${e.area}`:""})}}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),s&&this._syncAreaRelocations(s),this._syncActivityRuntime();const a=new Set(this._devices.filter(e=>!e.hidden).map(e=>e.id));for(const e of this._vacRt.keys())a.has(e)||this._vacRt.delete(e);if(this._infoCard){const e=this._devices.find(e=>e.id===this._infoCard.id);this._infoCard=e&&"ha_disabled"!==e.bindingStatus?.kind?e:null}const l=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==n.get(e.id));l&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._deviceDrag&&"ha_disabled"===this._devices.find(e=>e.id===this._deviceDrag.id)?.bindingStatus?.kind&&(this._cancelDeviceDrag(),this._devicePositionHistory.clear()),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(wf(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_resolveAreaRelocations(e=this._haRegistry){const t=function(e){const t=Yp(e.snapshot),i=new Set(Object.values(t).map(e=>e.binding)),n=new Map;for(const[t,r]of e.previousCandidates||[])Kp(t)&&Number.isFinite(r)&&i.has(t)&&n.set(t,r);const r=new Set,o=new Set;for(const t of e.markers||[])t.removed||(Gp(t.id)&&r.add(t.id),Kp(t.binding)&&o.add(t.binding));const s=new Set(Object.keys(e.registryDevices||{})),a=new Set(Object.keys(e.registryEntities||{})),l=new Set(Object.keys(e.liveStates||{})),c=new Map;for(const[e,i]of Object.entries(t)){const t=c.get(i.binding)||[];t.push(e),c.set(i.binding,t)}const h=new Set;let d=!1;for(const[t,i]of c){const[c,u]=em(t);if(o.has(t)||i.some(e=>r.has(e))||("device"===c?s.has(u):a.has(u)||l.has(u))){n.delete(t);continue}const p="device"===c?s.size>0:a.size>0;if(!e.authoritative||!p)continue;const m=n.get(t);if(void 0!==m){if(m!==e.revision)for(const e of i)h.add(e)}else n.set(t,e.revision),d=!0}return{removeIds:h,candidates:n,needsConfirmationRefresh:d}}({snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,revision:e.revision,registryDevices:e.devices,registryEntities:e.entities,liveStates:this.hass?.states,markers:this._markers,previousCandidates:this._areaSnapshotCleanupCandidates});return this._areaSnapshotCleanupCandidates=t.candidates,t.needsConfirmationRefresh&&this._canEdit&&ph(this.hass),tm({devices:this._devices,model:this._model,layout:this._layout,snapshot:this._settings.marker_area_snapshot,authoritative:e.authoritative,cleanupSnapshotIds:t.removeIds,coordinateScale:yf})}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const e=this._devices.filter(e=>!e.marker&&!e.virtual).map(e=>e.id).sort(),t=e.join(",");if(t===this._newSyncKey)return;this._newSyncKey=t;const i=this._settings,{fresh:n,known:r}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),n=e.filter(e=>!i.has(e));return{fresh:n,known:n.length?[...t,...n]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||n.length){const e=[...new Set([...i.new_device_ids||[],...n])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:r,new_device_ids:e}},this._saveConfig()}}_syncAreaRelocations(e){if(!(this._serverCfg&&this._norm&&this._canEdit&&this._haRegistry.authoritative))return;const t=e.decisions.filter(e=>e.updateSnapshot||e.removeSnapshot);if(!t.length)return;const i=El({snapshot:Yp(this._settings.marker_area_snapshot),decisions:t});i!==this._areaRelocationSyncKey&&(this._areaRelocationSyncKey=i,this._areaRelocationWrite=this._areaRelocationWrite.catch(()=>{}).then(async()=>{if(!this._serverCfg||!this._haRegistry.authoritative)return;const e=this._resolveAreaRelocations();this._areaRelocationIds=new Set(e.relocateIds);const t=new Set,i=new Map;let n=!1;for(const r of e.decisions){if(!r.relocate)continue;const e=T_(this._layout,r.id);try{await this._persistDevicePlacement(r.id,null),t.add(r.id),e&&i.set(r.id,e),this._areaRelocationIds.delete(r.id)}catch(t){n=!0,this._layout=D_(this._layout,r.id,e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}}const r=this._settings,o=function(e,t,i=new Set){const n=Yp(e);for(const e of t.decisions)e.removeSnapshot?delete n[e.id]:e.updateSnapshot&&e.binding&&e.area&&(e.relocate&&!i.has(e.id)||(n[e.id]={binding:e.binding,area:e.area}));return n}(r.marker_area_snapshot,e,t),s=[...new Set([...Array.isArray(r.new_device_ids)?r.new_device_ids:[],...t])],a=El(o)!==El(Yp(r.marker_area_snapshot)),l=El(s)!==El(Array.isArray(r.new_device_ids)?r.new_device_ids:[]);if(a||l){const e=r.marker_area_snapshot,n=r.new_device_ids,a={...this._serverCfg,settings:{...r,marker_area_snapshot:o,...l?{new_device_ids:s}:{}}};this._setAreaLifecycleConfig(a);try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.cancel(),await this._writeConfig()}catch(r){const a=this._settings;if(El(a.marker_area_snapshot)===El(o)&&El(a.new_device_ids)===El(l?s:n)){const t={...a};void 0===e?delete t.marker_area_snapshot:t.marker_area_snapshot=e,void 0===n?delete t.new_device_ids:t.new_device_ids=n;const i={...this._serverCfg,settings:t};this._setAreaLifecycleConfig(i),this._cfgContentFingerprint=El(this._serverCfg)}for(const e of t)this._areaRelocationIds.add(e);const c=new Set;for(const[e,t]of i)try{await this._persistDevicePlacement(e,t)}catch(t){c.add(e),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}this._areaRelocationSyncKey="",this._regSignature="";if("conflict"===(r&&"object"==typeof r&&"code"in r?r.code:void 0)?(this._showToast(this._t("toast.conflict")),await this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(r)})),c.size&&this._serverCfg){const e=this._settings,t=[...new Set([...Array.isArray(e.new_device_ids)?e.new_device_ids:[],...c])],i={...this._serverCfg,settings:{...e,new_device_ids:t}};this._setAreaLifecycleConfig(i);try{await this._writeConfig()}catch(e){this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(e)}))}}}}n&&(this._areaRelocationSyncKey=""),this.requestUpdate()}))}get _newIds(){const e=this._settings.new_device_ids;return new Set([...Array.isArray(e)?e:[],...this._areaRelocationIds])}_ackNewDevice(e){if(!this._newIds.has(e)||!this._serverCfg)return;const t=this._settings;this._serverCfg={...this._serverCfg,settings:{...t,new_device_ids:(t.new_device_ids||[]).filter(t=>t!==e)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(e){if(!e)return null;const t=[];for(const i of this._renderDevices){if(i.area!==e||i.virtual)continue;const n=Zh(this._renderPlanHass,i.entities);null!=n&&t.push(n)}return fi(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),n=Math.min(...t),r=Math.min(...i);return{x:n,y:r,w:Math.max(...t)-n,h:Math.max(...i)-r}}return{x:e.x??0,y:e.y??0,w:e.w??0,h:e.h??0}}_defaultPositions(){const e={},t=this._config?.icon_size??2.5;for(const i of this._model){const n=t/100*il(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const r=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!r.length)continue;const o=this._roomBounds(t),s=.1*Math.min(o.w,o.h),a=o.w-2*s,l=o.h-2*s,c=Math.max(1,Math.round(Math.sqrt(r.length*a/Math.max(l,1)))),h=Math.ceil(r.length/c),d=a/c,u=l/Math.max(h,1),p=r.map((e,t)=>({x:o.x+s+d*(t%c+.5),y:o.y+s+u*(Math.floor(t/c)+.5)}));yi(p,o,n,.5*s),r.forEach((t,i)=>e[t.id]=Ea(p[i]))}}return e}_pos(e){const t=this._renderDeviceSnapshot?.positions.get(e.id);return t?{x:t.x,y:t.y}:this._livePos(e)}_livePos(e){const t=this._spaceModelById(e.space),i=this._areaRelocationIds.has(e.id)?void 0:this._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*yf,y:i.y*yf}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Ea(tl(t)):{x:500,y:500}}_devicePlacementForCanvas(e,t,i){if(!this._norm)return{x:Math.round(t),y:Math.round(i)};const n=this._gridPitch,r=Math.round(t/n)*n,o=Math.round(i/n)*n;return{s:e.space,x:Ha(r/yf),y:Ha(o/yf)}}_previewDevicePlacement(e,t){this._layout=D_(this._layout,e,t),this.requestUpdate()}_cancelDeviceDrag(){const e=this._deviceDrag;if(!e)return!1;this._editorRuntime?._cancelPointerMove("device"),this._deviceDrag=null;try{e.source?.hasPointerCapture?.(e.pointerId)&&e.source.releasePointerCapture(e.pointerId)}catch{}return this._previewDevicePlacement(e.id,e.before),!0}async _persistDevicePlacement(e,t){if(this._layout=D_(this._layout,e,t),this._serverStorage){let i=null,n=!1;try{let r;if(null===t)i=null,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/delete",device_id:e});else{const t=Qr(this._layout[e]);El(t)!==El(this._layout[e])&&(this._layout={...this._layout,[e]:t}),i=t,this._sentPos.set(e,i),n=!0,r=await this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t})}this._noteLayoutRev(r),this._layoutContentFingerprint=El(this._layout),this._cacheSnapshot()}finally{n&&this._sentPos.get(e)===i&&this._sentPos.delete(e)}return}this._persistLocalLayout()}_devicePositionStateValid(e){const t=this._devices.find(t=>t.id===e.deviceId);return!!t&&t.space===e.spaceId&&"ha_disabled"!==t.bindingStatus?.kind&&!!this._spaceModelById(e.spaceId)}async _runDevicePositionHistory(e){if(this._cancelDeviceDrag()||this._devicePositionBusy)return;const t="undo"===e?this._devicePositionHistory.undo():this._devicePositionHistory.redo();if(!t)return;const i="undo"===e?t.before:t.after;if(!this._devicePositionStateValid(i))return this._devicePositionHistory.clear(),this._showToast(this._t("history.device_stale")),void this.requestUpdate();const n=T_(this._layout,i.deviceId);i.spaceId!==this._space&&(this._commitSpace(i.spaceId),this._restoreZoom()),this._devicePositionBusy=!0,this._previewDevicePlacement(i.deviceId,i.placement);try{await this._persistDevicePlacement(i.deviceId,i.placement),this._showToast(this._t("undo"===e?"history.undone":"history.redone",{name:t.name}))}catch(t){this._previewDevicePlacement(i.deviceId,n),"undo"===e?this._devicePositionHistory.redo():this._devicePositionHistory.undo(),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))}finally{this._devicePositionBusy=!1,this.requestUpdate()}}_undoDevicePosition(){this._runDevicePositionHistory("undo")}_redoDevicePosition(){this._runDevicePositionHistory("redo")}_savePos(e,t,i){this._spaceModelById(e.space)&&(this._layout=D_(this._layout,e.id,this._devicePlacementForCanvas(e,t,i)),this._dirtyPos.add(e.id),this._persistLayout())}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const n=i?null:this._renderDeviceSnapshot?.presentations.get(_p(e.id,t));return n||dp(this._renderPlanHass,e,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:t,designPreview:i,activityRuntime:this._activityRt.get(e.id),sourceDetails:!1,lightDevices:this._renderDevices,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion})}_deviceVisual(e){return this._devicePresentation(e).visual}_stateClass(e,t=this._deviceVisual(e)){const i=this._devicePresentation(e);return i.effectiveHidden?"":("icon_ripple"!==i.display||!1===this._config?.live_states||"alarm"===t.status||t.activity,hp({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=Qh(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:Jh(this._renderPlanHass,e.entities)}_bindingEntities(e){if("virtual"===e||!e)return[];const t=this._bindingStatus(e);return"active"===t.kind?t.enabledEntityIds:t.allEntityIds}_bindingHasClimate(e){return this._bindingEntities(e).some(e=>e.startsWith("climate."))}_bindingHasAlarm(e){return this._bindingEntities(e).some(e=>{const t=this._planHass?.states?.[e]||this.hass?.states?.[e],i=this._fullRegistryHass.entities[e]||this.hass?.entities?.[e];return Rn(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_deviceBindingActive(e,t=!0){if(e.virtual||"virtual"===e.bindingKind)return!0;if(!e.bindingKind||!e.bindingRef)return!1;const i=this._bindingStatus(`${e.bindingKind}:${e.bindingRef}`);return"active"===i.kind||(t&&this._showToast(this._t("ha_disabled"===i.kind?"toast.ha_disabled_action":"toast.ha_binding_unverified")),!1)}_openMoreInfo(e){e?this._planEntityAvailable(e)?(this._nativeMoreInfoEntity=e,wf(this,"hass-more-info",{entityId:e})):this._showToast(this._t("toast.ha_disabled_action")):this._showToast(this._t("toast.no_entity"))}_interruptViewGesture(e,t){if(clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),void 0!==e)for(const i of[t,this._stageEl])try{i?.hasPointerCapture?.(e)&&i.releasePointerCapture(e)}catch{}this._pointers.clear(),this._panStart=null,this._panLock=null,this._pinchStart=null,this._swipeStart=null,this._roomPointer=null,this._doubleFit.clear()}_closeInfoCard(){this._interruptViewGesture(),this._holdFired=!1,this._infoCard=null}_ctxDevice(e,t){"view"===this._mode&&(e.preventDefault(),e.stopPropagation(),this._deviceBindingActive(t)&&(t.primary?this._openMoreInfo(t.primary):this._infoCard=t))}_clickDevice(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if(e.stopPropagation(),this._deviceDrag?.moved||this._suppressClick||this._holdFired)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const n=Ld(i.tapAction,i.primary?.split(".")[0]);if("none"===n)return;const r=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===n){const e=this._toggleIntent(i);if(!e)return;if(!Vd(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=Vd(e);if(!t)return;if("virtual-light"===t.kind)return this._startDevicePressFeedback(i.id),void this.hass.callWS({type:"houseplan/virtual_light/toggle",marker_id:t.markerId}).then(e=>{const t=th(this._virtualLights,e);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}).catch(e=>this._showToast(this._t("toast.virtual_light_toggle_failed",{err:this._errText(e)})));const{command:n}=t;this._startDevicePressFeedback(i.id),this.hass.callService(n.domain,n.service,n.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},n=((o=e).targets.length?o.targets:o.skippedTargets).map(e=>e.name||e.entityId||e.ref).join(", ")||i.name;if(i.marker?.tap_confirm){const r=this._toggleConfirmationLines(e);if(!r.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:n}),lines:r,initialIntent:e,deviceId:i.id,exec:()=>{const n=this._devices.find(e=>e.id===i.id),r=n?this._toggleIntent(n):null;r&&!Vd(r)&&this._showUnavailableToggleTargets(r)||(r&&Kd(e,r)?t(r):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===n)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===n){const e=i.marker?.tap_target||"",t=function(e){const t=String(e||"").split(".")[0];return"automation"===t?{domain:"automation",service:"trigger"}:"script"===t?{domain:"script",service:"turn_on"}:"scene"===t?{domain:"scene",service:"turn_on"}:null}(e),n=this.hass.states[e];if(!t||!n)return void this._showToast(this._t("toast.run_target_missing"));const o=n.attributes?.friendly_name||e;return void r(this._t("confirm.tap_run",{name:o}),()=>{this._deviceBindingActive(i)&&this._planEntityAvailable(e)&&(this._startDevicePressFeedback(i.id),this.hass.callService(t.domain,t.service,{entity_id:e}).then(()=>{this._stampActivity(i.id,"event",this._activitySourceKey(i)),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)}))))})}"more-info"===n&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||Vd(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Gd.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const n of e.skippedTargets){if(!Gd.has(n.reason))continue;const e=String(n.name||n.entityId||n.ref||"").trim();e&&!i.has(e)&&(i.add(e),t.push(e))}return t}(e);return!!t.length&&(this._showToast(1===t.length?this._t("toast.toggle_target_unavailable",{name:t[0]}):this._t("toast.toggle_targets_unavailable",{names:t.join(", ")})),!0)}_keyDevice(e,t){"Enter"!==e.key&&" "!==e.key||"view"!==this._mode&&"devices"!==this._mode||(e.preventDefault(),this._clickDevice(e,t))}_startDevicePressFeedback(e){const t=[...this.renderRoot.querySelectorAll(".dev[data-id]")].find(t=>t.dataset.id===e),i=t?.querySelector(".device-shell");if(!i||"function"!=typeof i.animate)return;const n=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),r=Number.isFinite(n)?Math.max(.95,Math.min(1,n)):1;this._devicePressAnimations.get(e)?.cancel();const o=this._reducedMotion?[{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"},{outlineColor:"var(--hp-accent, #3ea6ff)",outlineStyle:"solid",outlineWidth:"2px",outlineOffset:"2px",offset:.5},{outlineColor:"transparent",outlineStyle:"solid",outlineWidth:"0px"}]:[{scale:String(r)},{scale:"0.95",offset:.5},{scale:"1"}],s=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,s);const a=()=>{this._devicePressAnimations.get(e)===s&&this._devicePressAnimations.delete(e)};s.addEventListener("finish",a,{once:!0}),s.addEventListener("cancel",a,{once:!0})}_cancelDevicePressFeedback(){for(const e of this._devicePressAnimations.values())e.cancel();this._devicePressAnimations.clear()}_pruneDevicePressFeedback(){if(!this._devicePressAnimations.size)return;const e=new Set([...this.renderRoot.querySelectorAll(".dev[data-id]")].map(e=>e.dataset.id||""));for(const[t,i]of this._devicePressAnimations)e.has(t)||(i.cancel(),this._devicePressAnimations.delete(t))}_t(e,t){return Su(xu(this.hass,this._config?.language),e,t)}get _colorPickerLabels(){return{title:this._t("color_picker.title"),hue:this._t("color_picker.hue"),saturation:this._t("color_picker.saturation"),value:this._t("color_picker.value"),hex:this._t("color_picker.hex"),invalidHex:this._t("color_picker.invalid_hex")}}_help(e){return this._editorRuntimeOrThrow()._help(e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(e){const t=[];for(const i of this._devices){if(i.space!==e.id||i.hidden)continue;const n=this._pos(i);t.push({minX:n.x,minY:n.y,maxX:n.x,maxY:n.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,n=Math.cos(i)*e.rlen/2,r=Math.sin(i)*e.rlen/2,o=Wa([[e.rx-n,e.ry-r],[e.rx+n,e.ry+r]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){if("image"===e.kind&&!this._decorAssets.has(e.asset_id)&&"decor"!==this._mode)continue;const n=Wa("line"===e.kind?[[e.x1*yf,e.y1*i],[e.x2*yf,e.y2*i]]:"text"===e.kind?[[e.x*yf,e.y*i]]:mr({x:e.x*yf,y:e.y*i,w:e.w*yf,h:e.h*i,angle:e.angle}));n&&t.push(n)}for(const i of this._physicalBodiesR(e)){const e=Wa(i);e&&t.push(e)}}return Ka(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:yf,h:yf};return{rect:e,all:e,outliers:0}}const t=this._frame,i="view"!==this._mode;if(t&&t.id===e.id&&this._bdDrag)return t;if(t&&t.id===e.id&&t.model===e&&t.layout===this._layout&&t.devs===this._devices&&t.far===this._showFar&&t.grow===i)return t;const n=Ja(this._contentItems(e));let r=n.all||el(e),o=this._showFar?r:n.core||el(e);return t&&t.id===e.id&&i&&t.grow&&(o=bf(t.rect,o),r=bf(t.all,r)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:r,outliers:n.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),n=this._openingsR.map((e,t)=>({id:String(e.id||t),sourceIndex:t,type:e.type,x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen>0?e.rlen:900,flipH:!!e.flip_h,flipV:!!e.flip_v})),r=ba(ig,this._cellCm),o=ba(10,this._cellCm),s=`${e.id}|${function(e){let t=2166136261;const i=e=>{if("number"!=typeof e)if("string"!=typeof e)if("boolean"!=typeof e)if(Array.isArray(e)){t=qg(t,e.length);for(const t of e)i(t)}else{if(e&&"object"==typeof e){const t=e;for(const e of Object.keys(t).sort())i(e),i(t[e]);return}t=qg(t,-1)}else t=qg(t,e?1:0);else for(const i of e)t=qg(t,i.codePointAt(0)||0);else t=qg(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:n,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:tg,wallHeight:r,floorEdgeHeight:o,algorithm:3})}`;return{key:s,build:()=>{const o=sa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,s=this._roomWallOpeningInputs(this._openingsR,e),a=t.length||o.length?Ps(e.rooms,t,i,s,this._wallKeyPitch,this._cellCm,this._gridPitch,yf,o):null;if(a&&("failed-core"===a.status||"not-applicable"===a.status))throw new Error("wall boolean geometry failed");const l=a?.paperGeom??Ts(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf);if(!l)throw new Error("floor boolean geometry failed");const c=a?.openingIndex||Hs(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf),h=n.map(e=>{const i=this._openingsR[e.sourceIndex],n="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?nu(i.partitionHost,n):t.length||"gate"===e.type?Ws(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:n}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(Wg([e.x,e.y,e.angle,e.length,e.face.ox,e.face.oy,t])&&e.length>0&&t>0))throw new Error("invalid isometric opening input");const i=e.length/2;let n;if("passage"===e.type)n=[];else if("gate"===e.type){const r=10*e.face.side;n=[Ug(e,0,[-i,0],[i,0],r,0,.88*t),Ug(e,1,[i,0],[-i,0],-r,0,.88*t)]}else n="window"===e.type?[Ug(e,0,[-i,0],[i,0],-90,.27*t,.78*t),Ug(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[Ug(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(n.map(e=>Object.freeze(e)))}}({...e,face:o},r)});return{walls:a?.components.flatMap(e=>e.geom)||[],floor:l,openings:Object.freeze(h)}}}}_isoSceneKey(){if(!this._labsIso||"view"!==this._mode)return null;try{return this._isoSource()?.key??null}catch{return`${this._space}|invalid`}}_isoScene(){const e=this._isoSource();if(!e)return null;const t=this._isoGeometryCache.get(e.key);if(t)return{key:e.key,...t};const i=this._frameOf().rect,n=e.build(),r=ba(ig,this._cellCm),o=ba(10,this._cellCm),s=function(e){let t=1/0,i=1/0,n=-1/0,r=-1/0;for(const o of e)for(const e of o.leaves){const o=Math.hypot(e.closedVector[0],e.closedVector[1]);t=Math.min(t,e.hinge[0]-o),i=Math.min(i,e.hinge[1]-o),n=Math.max(n,e.hinge[0]+o),r=Math.max(r,e.hinge[1]+o)}return Wg([t,i,n,r])?{x:t,y:i,w:n-t,h:r-i}:null}(n.openings),a=ag({rect:s?bf(i,s):i,wallHeight:r,openingHeight:r,floorDepth:o}),l=function(e,t=tg,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const n=[],r=[],o=[];let s=0;for(let a=0;a<(e||[]).length;a++){const l=e[a];for(let e=0;e<(l||[]).length;e++){const c=Ng(l[e],e>0);if(!(c.length<3||Math.abs(Eg(c))<1e-9)){n.push(Hg(c,t,i)),s+=c.length;for(let n=0;ne.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:n.join(" "),sides:r,contactPath:o.join(" "),edgeCount:s}}(n.walls,tg,r),c={geometry:l,floor:Bg(n.floor,o),openings:n.openings,frame:a};return bg(this._isoGeometryCache,e.key,c,8),{key:e.key,...c}}_effectiveProjection(){if("iso"!==this._desiredProjection||!this._model.length)return"flat";const e=this._isoSceneKey()||`${this._space}|invalid`;if(this._isoFallback.has(e))return"flat";try{const e=this._isoScene();return e?(t="iso",i=e.key,n=this._isoFallback,"iso"===t&&i&&!n.has(i)?"iso":"flat"):"flat"}catch(t){if(!this._isoFallback.has(e)){this._isoFallback.add(e);const i=e.split("|");console.warn(`HOUSEPLAN ISO FALLBACK: #89, space ${this._space}, fingerprint ${i[i.length-1]}, ${t instanceof Error?t.message:"renderer error"}`)}return"flat"}var t,i,n}_scenePoint(e){return"iso"===this._renderProjection?rg(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=og([e.x,e.y]),i=og([e.x+e.w,e.y+e.h]);return{x:t[0],y:t[1],w:i[0]-t[0],h:i[1]-t[1]}}_baseVb(){if("iso"===this._effectiveProjection()){if(!this._spaceDisplayForRender().showBorders){const e=ag({rect:this._frameOf().rect,wallHeight:ba(ig,this._cellCm)});return[e.x,e.y,e.w,e.h]}const e=this._isoScene()?.frame??this._frameOf().rect;return[e.x,e.y,e.w,e.h]}const e=this._frameOf().rect;return[e.x,e.y,e.w,e.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom()}_clearRoomFocus(e=!1){this._doubleFit.clear(),this._roomFocus=null,e&&(this._roomPointer=null)}_roomFitBounds(e,t){const i=Ht(e);if(!i||!e.id)return null;const n=this._spaceWalls,r=this._openCuts(),o=this._wallUnionGeometry(),s=n.length&&this._innerRoomContour(t,e.id,r,o?.roomGeom,o?.multiWallNodes)||i,a=this._spaceDisplayForRender();let l=[];if(a.showBorders&&n.length){const i=hs(t.rooms,e.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,yf);i&&(l=Ns(i.poly,i.offsets,o?.multiWallNodes)||i.poly)}const c=this._effectiveProjection();return function(e){const t=e.floor,i=e.wall||[],n=e.padding??0;if(!t.length||!t.every(pg)||!i.every(pg)||!Number.isFinite(n)||n<0)return null;if("flat"===e.projection)return _g([...t,...i],n);const r=e.wallHeight??0,o=e.floorDepth??0;if(!Number.isFinite(r)||r<0||!Number.isFinite(o)||o<0)return null;try{const e=[];for(const i of t){const t=[i[0],i[1]];e.push(rg(t,0)),o>0&&e.push(rg(t,-o))}for(const t of i){const i=[t[0],t[1]];e.push(rg(i,0)),r>0&&e.push(rg(i,r))}return _g(e,n)}catch{return null}}({floor:s,wall:l,projection:c,wallHeight:"iso"===c&&a.showBorders?ba(ig,this._cellCm):0,floorDepth:"iso"===c&&a.showBorders?ba(10,this._cellCm):0,padding:a.showBorders?ba(1.25,this._cellCm):0})}_fitRoom(e,t=!0){if("view"!==this._mode)return!1;this._doubleFit.clear();const i=this._spaceModel(),n=i?.rooms.find(t=>t.id===e);if(!i||!n)return this._clearRoomFocus(),!1;const r=this._roomFitBounds(n,i);if(!r)return this._clearRoomFocus(),!1;this._roomFocus={spaceId:i.id,roomId:e};const o=this._stageEl;if(!o||o.clientWidth<=0||o.clientHeight<=0)return!0;const s=vi(this._baseVb(),o.clientWidth/o.clientHeight),a=function(e){const t=e.safeFraction??.8,{bounds:i,baseFit:n,stageWidth:r,stageHeight:o,minZoom:s,maxZoom:a}=e;if(!mg(i)||!mg(n)||!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||!Number.isFinite(s)||s<=0||!Number.isFinite(a)||a1)return null;const l=r/o,c=Math.max(i.w/t,i.h/t*l);if(!Number.isFinite(c)||c<=0)return null;const h=Math.min(a,Math.max(s,n.w/c)),d=n.w/h,u=d/l;return{zoom:h,viewBox:{x:i.x+i.w/2-d/2,y:i.y+i.h/2-u/2,w:d,h:u}}}({bounds:r,baseFit:s,stageWidth:o.clientWidth,stageHeight:o.clientHeight,minZoom:Mf.ZOOM_MIN,maxZoom:Mf.ZOOM_MAX});if(!a)return this._clearRoomFocus(),!1;const l=this._cameraState(),c=function(e,t,i,n){const r=[e,t,i,n],o=Math.min(...r.map(e=>e.x)),s=Math.min(...r.map(e=>e.y));return{x:o,y:s,w:Math.max(...r.map(e=>e.x+e.w))-o,h:Math.max(...r.map(e=>e.y+e.h))-s}}(s,l.viewBox,a.viewBox,r);return a.viewBox=this._clampView(a.viewBox,c),t?(this._prepareCameraCommand(),this._startCameraTransition(a,c,"room",220),!0):(this._cancelCameraTransition(!1),Z_(l,a)||(this._zoom=a.zoom,this._view={...a.viewBox},this.requestUpdate()),!0)}_fitAll(e="fit"){this._clearRoomFocus(),this._showFar=!0,this._frame=null,this.requestUpdate(),this._resetZoom(e)}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?V:W`
    ${this._t("canvas.far_objects",{n:this._outliers})} @@ -4956,89 +4956,89 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef style="left:${a.toFixed(1)}%;top:${l.toFixed(1)}%" @click=${e=>{e.stopPropagation(),this._fitAll("home")}}> - `}_stageAspect(){const e=this._stageEl,t=this._baseVb();return e&&e.clientHeight?e.clientWidth/e.clientHeight:t[2]/t[3]}_viewOr(e){return this._view&&this._view.w?this._view:vi(e,this._stageAspect())}_roomLabelReferenceViewWidth(e){if(!this._markup)return e.w;const t=this._stageEl,i=this.renderRoot.querySelector(".editorchrome");if(!t||t.clientWidth<=0||t.clientHeight<=0)return e.w;const n=t.clientHeight+(i?.getBoundingClientRect().height||0);return n<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,n).w}_screenToVb(e,t){const i=this._stageEl,n=this._viewOr(this._baseVb()),r=i?.clientWidth||1,o=i?.clientHeight||1;return[n.x+e/r*n.w,n.y+t/o*n.h]}_clampView(e,t){const i=(e,t,i,n)=>{const r=1*Math.max(t,n),o=i-r,s=i+n-t+r;return Math.max(Math.min(o,s),Math.min(Math.max(o,s),e))};return{w:e.w,h:e.h,x:i(e.x,e.w,t.x,t.w),y:i(e.y,e.h,t.y,t.h)}}_applyView(e,t,i){this._cancelCameraTransition(!1);const n=this._baseVb(),r=vi(n,this._stageAspect()),o=Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,e)),s=r.w/o,a=r.h/o,l=this._viewOr(n),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-s/2,y:h-a/2,w:s,h:a},r);return!(this._view&&Math.abs(this._zoom-o)<1e-9&&Math.abs(this._view.x-d.x)<1e-6&&Math.abs(this._view.y-d.y)<1e-6&&Math.abs(this._view.w-d.w)<1e-6&&Math.abs(this._view.h-d.h)<1e-6)&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=o,this._view=d,!0)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const e=()=>{if(!this._booting)return;const t=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=t);const n=t-this._bootStart;n>=1200||n>=700&&i>0&&t-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(e,100)};this._bootTimer=window.setTimeout(e,100)}_bootSettled(){this._booting&&!this._bootSettling&&(this._bootSettling=!0,this._refitView(),this._bootSettleRaf=requestAnimationFrame(()=>{this._bootSettleRaf=0,this._finishBootSettled()}))}_finishBootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1,this._bootSettling=!1;const e=this._stageEl?.clientHeight??0;!this._config?.kiosk&&e>0&&this._warmPatch({hdrH:this._hdrH,stageH:e,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},rf)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._booting||this._resumeSettling||(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0),this._viewportInvalidAt=0,this._beginContinuityCandidate("warm-resume",!1),this._loading?this.requestUpdate():this._loadFromServer())}_refitView(){if(this._modeTransitionBusy||this._warmModeRequest)return;this._cancelCameraTransition(!1);const e=this._stageEl;if(!e||"visible"!==document.visibilityState||e.clientWidth<=0||e.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));const t=[e.clientWidth,e.clientHeight],i=this._lastValidStageSize,n=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,r=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._roomFocus?.spaceId===this._space?this._fitRoom(this._roomFocus.roomId,!1):this._view||this._applyView(this._zoom));n?this._pendingRefitSize=null:(this._pendingRefitSize=t,this._refitRaf||(this._refitRaf=requestAnimationFrame(()=>{this._refitRaf=0;const e=this._pendingRefitSize;this._pendingRefitSize=null;const t=this._stageEl;if(!e||!t||t.clientWidth<=0||t.clientHeight<=0)return;if(Math.abs(t.clientWidth-e[0])>.5||Math.abs(t.clientHeight-e[1])>.5)return void this._refitView();const i=this._lastValidStageSize;if(i&&Math.abs(i[0]-e[0])<=.5&&Math.abs(i[1]-e[1])<=.5)return;this._lastValidStageSize=e;const n=this._view;r>=Dl?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._roomFocus?.spaceId!==this._space?this._applyView(this._zoom,n?n.x+n.w/2:void 0,n?n.y+n.h/2:void 0):this._fitRoom(this._roomFocus.roomId,!1)})))}_cameraTargetAt(e,t,i,n=!1){const r=this._stageEl;if(!r||r.clientWidth<=0||r.clientHeight<=0)return null;const o=vi(this._baseVb(),this._stageAspect()),s=Math.min(kf.ZOOM_MAX,Math.max(kf.ZOOM_MIN,i)),a=function(e,t,i,n,r,o,s){if(!U_(e)||!U_({zoom:t,viewBox:i})||!j_(n)||n<=0||!j_(r)||r<=0||!j_(o)||!j_(s))return null;const a=o/n,l=s/r,c=e.viewBox.x+a*e.viewBox.w,h=e.viewBox.y+l*e.viewBox.h,d=i.w/t,u=i.h/t;return{zoom:t,viewBox:{x:c-a*d,y:h-l*u,w:d,h:u}}}(n&&this._cameraTransition.target||this._cameraState(),s,o,r.clientWidth,r.clientHeight,e,t);return a?(a.viewBox=this._clampView(a.viewBox,o),{target:a,fit:o}):null}_zoomAt(e,t,i){this._clearRoomFocus(),this._cancelCameraTransition(!1);const n=this._cameraTargetAt(e,t,i);n&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=n.target.zoom,this._view={...n.target.viewBox})}_onWheel(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),n=e.deltaY<0?1.15:1/1.15,r=this._cameraTransition.target?.zoom??this._zoom,o=this._cameraTargetAt(e.clientX-i.left,e.clientY-i.top,r*n,!0);o&&this._startCameraTransition(o.target,o.fit,"wheel",160)}_stepZoom(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;const i=this._cameraTransition.target?.zoom??this._zoom,n=this._cameraTargetAt(t.clientWidth/2,t.clientHeight/2,i*(e>0?1.4:1/1.4),!0);n&&this._startCameraTransition(n.target,n.fit,"button",180)}_resetZoom(e="fit"){this._clearRoomFocus(),this._prepareCameraCommand();const t=vi(this._baseVb(),this._stageAspect());this._startCameraTransition({zoom:1,viewBox:{...t}},t,e,220)}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(hf,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){this._clearRoomFocus(!0),this._cancelCameraTransition(!1);const e=this._zoomBySpace[this._space]||1;this._zoom=e;const t=this._stageEl;if(t&&t.clientHeight){const t=this._baseVb();return this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const t=this._baseVb();this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),this.requestUpdate()})}_stagePointerDown(e){const t=e.isPrimary&&0===e.button&&"view"===this._mode?ag(e.composedPath()):null;if(this._roomPointer=t?{pointerId:e.pointerId,spaceId:this._space,roomId:t}:null,this._doubleFit.pointerDown(e,this._space,this._doubleFitEnabled),this._cancelCameraTransition(!1,!0),this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:e.clientX,y:e.clientY,id:e.pointerId},e.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._roomPointer=null,this._doubleFit.clear(),this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag||this._deviceDrag)return;if(this._markup&&e.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&e.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(e))return;this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY});const i=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:i.x,vy:i.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y);this._pinchStart={dist:t,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return!this._hasFixedFloor&&this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(e){if(this._physicalRotate?.pid!==e.pointerId)if(this._physicalDrag?.pid!==e.pointerId)if(this._dtDrag?.pid!==e.pointerId)if(this._bdDrag?.pid!==e.pointerId){if(this._decorDraft?.pid===e.pointerId){const t=this._decorDraft;let i=this._decorSnap(this._svgPoint(e),e.pointerType);if(e.shiftKey&&("rect"===t.kind||"ellipse"===t.kind)){const e=i[0]-t.a[0],n=i[1]-t.a[1],r=Math.max(Math.abs(e),Math.abs(n));i=this._snap([t.a[0]+(e<0?-r:r),t.a[1]+(n<0?-r:r)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId){if("decor"!==this._mode||"furniture"!==this._decorTool&&"image"!==this._decorTool||!this._editorRuntime||(this._notePointer(e),!this._editorRuntime._furnPointerMove(e,this._pointerModality.hoverEnabled)))if(this._pointers.has(e.pointerId)){if(this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(e),this._pinchStart&&this._pointers.size>=2){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),n=(e[0].x+e[1].x)/2-i.left,r=(e[0].y+e[1].y)/2-i.top;this._zoomAt(n,r,this._pinchStart.zoom*t),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const t=e.clientX-this._panStart.sx,i=e.clientY-this._panStart.sy;Math.abs(t)+Math.abs(i)>4&&(this._roomPointer=null,this._doubleFit.clear(),this._suppressClick=!0,clearTimeout(this._holdTimer),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover()),null===this._panLock&&Math.abs(t)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(t)>1.5*Math.abs(i)?"swipe":"pan","pan"===this._panLock&&this._clearRoomFocus());const n=this._stageEl;if("pan"===this._panLock&&n){const e=this._baseVb(),r=this._viewOr(e),o=vi(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(n.clientWidth||1)*r.w,y:this._panStart.vy-i/(n.clientHeight||1)*r.h,w:r.w,h:r.h},o)}}}else this._markupMove(e)}else this._decorMoveUpdate(e)}else this._bdMove(e);else this._dtMove(e);else this._physicalMove(e);else this._physicalRotateMove(e)}_stagePointerLeave(e){"decor"===this._mode&&this._editorRuntime?._furnPointerLeave(e),this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){const t=(i=this._roomPointer,n=e.pointerId,r=this._space,o=this._roomPointer&&"view"===this._mode?ag(e.composedPath()):null,s=this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired||this._touchSequenceMultitouch,!i||s||i.pointerId!==n||i.spaceId!==r||i.roomId!==o?null:i.roomId);var i,n,r,o,s;this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null);if(this._doubleFit.pointerUp(e,this._space,this._doubleFitEnabled,this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired)&&this._fitAll("double-tap"),this._kiosk){clearTimeout(this._kioskHoldTimer);const i=this._swipeStart;if(this._swipeStart=null,!t&&i&&i.id===e.pointerId){const t=e.clientX-i.x,n=e.clientY-i.y,r="pan"===this._panLock?null:function(e,t,i,n,r,o=60){if(i>1.001||n.length<2)return null;if(Math.abs(e)e.id),this._space);r&&this._slideTo(r,t<0?"left":"right")&&(this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid===e.pointerId)return void this._physicalUp(e);if(this._physicalRotate?.pid===e.pointerId)return void this._physicalRotateUp(e);if(this._dtDrag?.pid===e.pointerId)return void this._dtUp();if(this._bdDrag?.pid===e.pointerId)return void this._bdUp();if(this._decorDraft?.pid===e.pointerId)return void this._decorCommitDraft();if(this._decorMove?.pid===e.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);if(this._editorRuntime?._furnPointerUp(e))return;const a=!!this._pinchStart||"pan"===this._panLock;this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._suppressClick&&setTimeout(()=>this._suppressClick=!1,0)),a&&0===this._pointers.size&&!t&&this.requestUpdate(),t&&this._fitRoom(t)}_clickRoom(e){!this._suppressClick&&e.area&&vf("/config/areas/area/"+e.area)}_roomLabelKey(e,t){"view"!==this._mode||"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),e.stopPropagation(),this._fitRoom(t))}_pointerDown(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,n=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,n),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind||this._devicePositionBusy)return;if(this._deviceDrag)return void(this._deviceDrag.pointerId!==e.pointerId&&this._cancelDeviceDrag());e.preventDefault();const i=this._pos(t);this._deviceDrag={id:t.id,spaceId:t.space,displayName:t.name,pointerId:e.pointerId,source:e.currentTarget,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1,before:C_(this._layout,t.id),start:this._devicePlacementForCanvas(t,i.x,i.y)},bf(e),this._tip=null}_pointerMove(e,t){if("devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;const n=this.renderRoot.querySelector(".stage");if(!n)return;const r=this._baseVb(),o=n.getBoundingClientRect(),s=this._viewOr(r),a=(e.clientX-i.sx)/o.width*s.w,l=(e.clientY-i.sy)/o.height*s.h;Math.abs(e.clientX-i.sx)+Math.abs(e.clientY-i.sy)>3&&(i.moved=!0,clearTimeout(this._holdTimer));const c=Ea(i.ox+a),h=Ea(i.oy+l);this._previewDevicePlacement(t.id,this._devicePlacementForCanvas(t,c,h))}_pointerUp(e,t){if(clearTimeout(this._holdTimer),"devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;this._deviceDrag=null;const n=C_(this._layout,t.id);var r,o;i.moved&&null!==n&&(r=n,o=i.start,null===r||null===o?r!==o:r.x!==o.x||r.y!==o.y||r.s!==o.s)?(this._selId=t.id,this._suppressClick=!0,window.setTimeout(()=>{this._suppressClick=!1},0),this._devicePositionBusy=!0,this.requestUpdate(),this._persistDevicePlacement(t.id,n).then(()=>{const e=this._t("history.device_move",{name:i.displayName});this._devicePositionHistory.push({name:e,before:{deviceId:i.id,spaceId:i.spaceId,placement:i.before},after:{deviceId:i.id,spaceId:i.spaceId,placement:n}})}).catch(e=>{this._previewDevicePlacement(t.id,i.before),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))}).finally(()=>{this._devicePositionBusy=!1,this.requestUpdate()})):this._previewDevicePlacement(t.id,i.before)}_pointerCancel(e,t){this._deviceDrag?.id===t.id&&this._deviceDrag.pointerId===e.pointerId&&this._cancelDeviceDrag()}_showToast(e){for(const e of this.renderRoot.querySelectorAll("hp-dialog"))e.closeTransientOverlays("toast");this._toast=e,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}_syncPointerHoverTargets(){const e=this._pointerModality.hoverEnabled;for(const t of this.renderRoot.querySelectorAll(mf))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(mf)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(mf))e.toggleAttribute("data-pointer-hover",i)}_clearTransientHover(e=!1){e&&this._pointerModality.suspend(),this._tip&&(this._tip=null),this._hoverRoom&&(this._hoverRoom=null)}_notePointer(e){const t=this._pointerModality.modality,i=this._pointerModality.note(e);"touch"!==i&&"pen"!==i||t===i&&!this._tip&&!this._hoverRoom||this._clearTransientHover()}_guardTouchGesture(e){if(this._editorSecondary?.handleOutsideDismiss(e))return;if("click"===e.type){if(!this._suppressClick&&!this._touchSequenceMultitouch&&Date.now()>this._touchClickBlockUntil)return;return e.preventDefault(),void e.stopImmediatePropagation()}const t=e;if(this._notePointer(t),"pointercancel"===e.type||"lostpointercapture"===e.type?this._doubleFit.clear():"pointerdown"===e.type&&this._doubleFit.clearOutside(t),"touch"===t.pointerType)if("pointerdown"!==e.type){if("pointermove"===e.type){const e=this._touchContacts.get(t.pointerId);if(!e)return;const i={...e,x:t.clientX,y:t.clientY};if(this._touchContacts.set(t.pointerId,i),this._touchSequenceMultitouch&&"view"===this._mode&&!this._vacFit&&this._pinchStart&&[...this._touchContacts.values()].every(e=>e.inStage)){this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=[...this._touchContacts.values()];if(e.length>=2&&this._stageEl){const[t,i]=e,n=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),r=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-r.left,(t.y+i.y)/2-r.top,this._pinchStart.zoom*n),this._saveZoom()}}return}if("pointerup"===e.type||"pointercancel"===e.type||"lostpointercapture"===e.type){if(this._clearTransientHover(),this._touchContacts.delete(t.pointerId),this._touchSequenceMultitouch){if(this._touchClickBlockUntil=Date.now()+500,this._pointers.delete(t.pointerId),!this._vacFit&&this._pointers.size>=2){const[e,t]=[...this._pointers.values()];this._pinchStart={dist:Math.hypot(e.x-t.x,e.y-t.y),zoom:this._zoom}}else this._pinchStart=null;0===this._pointers.size&&(this._panStart=null,this._panLock=null)}0===this._touchContacts.size&&(this._touchSequenceMultitouch=!1)}}else if(this._touchContacts.set(t.pointerId,{x:t.clientX,y:t.clientY,inStage:!!t.target?.closest?.(".stage")}),this._touchContacts.size>=2){this._clearRoomFocus(!0),this._clearTransientHover(),this._touchSequenceMultitouch=!0,this._touchClickBlockUntil=Number.POSITIVE_INFINITY,clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),this._swipeStart=null;const e=[...this._touchContacts.values()];if("view"===this._mode&&!this._vacFit&&e.every(e=>e.inStage)){this._pointers=new Map([...this._touchContacts].map(([e,t])=>[e,{x:t.x,y:t.y}]));const[t,i]=e;this._pinchStart={dist:Math.hypot(t.x-i.x,t.y-i.y),zoom:this._zoom},this._panStart=null,this._panLock=null}else this._vacFit&&(this._pointers.clear(),this._pinchStart=null,this._panStart=null,this._panLock=null)}}_showTip(e,t,i,n,r,o,s=!1){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||this._deviceDrag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:n,temp:r,hum:o,room:s}))}_roomTipEnabledForPointer(e){return"view"===this._mode&&(this._notePointer(e),!!Ki(this._settings)||(this._tip?.room&&(this._tip=null),!1))}get _gridPitch(){return Pa}get _cellCm(){const e=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(e)&&e>0?e:5}_fmtLen(e,t){const i=function(e,t,i,n){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*n}(e,t,this._gridPitch,this._cellCm);return Et(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const e=this._resize?.preview;return e&&e.space===this._space?e.sp:this._serverCfg?.spaces.find(e=>e.id===this._space)}get _renderCfg(){const e=this._resize?.preview;return e&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(t=>t.id===e.space?e.sp:t)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,_f}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return Bt(e?.rooms||[]).map(e=>[e[0]*_f,e[1]*t,e[2]*_f,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(df)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(df,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,this._clearRoomFocus(!0),this._cancelDangerConfirm(),"view"!==this._mode&&this._setMode("view",!1),this._pendingNavMode=null,this._geometryHistory.clear(),this._activeDraftId=null,this._resumeDraftBySpace={},this._draftSegmentCms=[],this._closingWallCm=null,this._drawWallField=null,this._showHidden=!1,this._tapConfirm=null,this._vacCalConfirm=null,this._decorEraseConfirm=null,this._openingInfo=null,this._closeInfoCard(),this._rulesDialog=null,this._alignDialog=null,this._preflightClipboardFallback=null,this._backupImportDialog=null,this._backupExportDialog=null,this._supportDialog?.preview?.token&&this._editorRuntime?._discardSupportPreview(this._supportDialog.preview.token),this._supportDialog=null,this._settingsDialog=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){return e!==this._mode&&(this._clearRoomFocus(!0),this._cancelDangerConfirm()),this._warmModeRequest=0,this._editorRuntime?this._editorRuntime._setMode(e,t):"view"===e?void this._editorModeRequest++:void this._requestMode(e,t)}_primeDrawWallField(){return this._editorRuntimeOrThrow()._primeDrawWallField()}get _drawWallFieldValue(){return null===this._drawWallField?uo(15,this._imperial):this._drawWallField}get _drawWallCm(){const e=(e=>{const t=String(e??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t))return null;const i=Number(t);return Number.isFinite(i)?i:null})(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Vs:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Vs:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){return this._editorRuntimeOrThrow()._showPhysicalRange(e,t)}_draftSegmentCount(e=this._curSpaceCfg){return this._editorRuntimeOrThrow()._draftSegmentCount(e)}_mergeSpacePartitions(e,t){return this._editorRuntimeOrThrow()._mergeSpacePartitions(e,t)}_finishWallChain(){return this._editorRuntimeOrThrow()._finishWallChain()}_activateMarkupTool(e){return this._editorRuntimeOrThrow()._activateMarkupTool(e)}_limitReached(e){return this._editorRuntimeOrThrow()._limitReached(e)}_svgPoint(e){return this._editorRuntimeOrThrow()._svgPoint(e)}_snap(e){return this._editorRuntimeOrThrow()._snap(e)}_snapDrawPoint(e,t=!1){return this._editorRuntimeOrThrow()._snapDrawPoint(e,t)}_planSnapOpeningCuts(e,t){return this._editorRuntimeOrThrow()._planSnapOpeningCuts(e,t)}_planSnapGeometrySnapshot(){return this._editorRuntimeOrThrow()._planSnapGeometrySnapshot()}_hiddenWallDiagnosticSnapshot(){return this._editorRuntimeOrThrow()._hiddenWallDiagnosticSnapshot()}_planStructuralGeometrySnapshot(){return this._editorRuntimeOrThrow()._planStructuralGeometrySnapshot()}_planSnapContextKey(e){return this._editorRuntimeOrThrow()._planSnapContextKey(e)}_resolvePlanDrawPoint(e,t){return this._editorRuntimeOrThrow()._resolvePlanDrawPoint(e,t)}get _activePlanSnapCandidate(){if(!this._markup||"draw"!==this._tool)return null;const e=this._planSnapHover;if(!e)return null;const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.candidate:null}get _activePlanSnapConflicts(){if(!this._markup||"draw"!==this._tool)return[];const e=this._planSnapHover;if(!e)return[];const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.conflicts:[]}_clearPlanSnapHover(e=!0){return this._editorRuntime?this._editorRuntimeOrThrow()._clearPlanSnapHover(e):(this._planSnapHover=null,void(e&&(this._cursorPt=null)))}_samePt(e,t){return Xt(e,t)}_dropLegacySegments(){return this._editorRuntimeOrThrow()._dropLegacySegments()}_rollbackRejectedPhysicalWrites(e){return this._editorRuntimeOrThrow()._rollbackRejectedPhysicalWrites(e)}async _reloadRejectedPhysicalWrite(){return this._editorRuntimeOrThrow()._reloadRejectedPhysicalWrite()}get _cfgWriting(){return this._writesPending>0}async _sendConfigCandidate(e){const t=to(e),i=await this.hass.callWS({type:"houseplan/config/set",config:t,expected_rev:this._cfgRev});this._cfgRev=i?.rev??this._cfgRev+1}_writeConfig(){if(this._editorRuntime)return this._editorRuntime._writeConfig();this._writesPending++,this._writeChain=x_(this._writeChain,async()=>{if(!this._serverCfg)return;const e=to(this._serverCfg),t=Il(e);t!==Il(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t,await this._sendConfigCandidate(e)});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){return this._editorRuntimeOrThrow()._geometrySnapshotFromConfig(e,t)}_geometrySnapshot(e=this._space){return this._editorRuntimeOrThrow()._geometrySnapshot(e)}_recordGeometry(e,t){return this._editorRuntimeOrThrow()._recordGeometry(e,t)}_restoreGeometryStateInConfig(e,t,i=!1){return this._editorRuntimeOrThrow()._restoreGeometryStateInConfig(e,t,i)}_restoreGeometryStateLocal(e){return this._editorRuntimeOrThrow()._restoreGeometryStateLocal(e)}_wallModelBlockerLabel(e){return this._editorRuntimeOrThrow()._wallModelBlockerLabel(e)}_hasLegacyZeroWallFields(e=this._serverCfg){return this._editorRuntimeOrThrow()._hasLegacyZeroWallFields(e)}_showWallModelMigrationBlocked(e){return this._editorRuntimeOrThrow()._showWallModelMigrationBlocked(e)}_limitSegmentsOf(e){return this._editorRuntimeOrThrow()._limitSegmentsOf(e)}_junctionLimitViolations(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitViolations(e,t,i)}_junctionLimitLabel(e){return this._editorRuntimeOrThrow()._junctionLimitLabel(e)}_junctionLimitsIntroduced(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitsIntroduced(e,t,i)}_commitPhysicalGeometry(e,t,i=[]){return this._editorRuntimeOrThrow()._commitPhysicalGeometry(e,t,i)}_clearGeometryGesture(){return this._editorRuntime?this._editorRuntimeOrThrow()._clearGeometryGesture():(this._path=[],this._planSnapHover=null,this._cursorPt=null,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingPreset=null,this._openingRebindId=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._roomDeleteDialog=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._resize?.reset(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,void(this._bdDrag=null))}_stagePointerCancel(e){if(this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null),this._doubleFit.clear(),this._editorRuntime)return this._editorRuntime._stagePointerCancel(e);const t=!!this._pinchStart||!!this._panStart;this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._swipeStart=null),t&&0===this._pointers.size&&this.requestUpdate()}_applyGeometryState(e,t=!1){return this._editorRuntimeOrThrow()._applyGeometryState(e,t)}_roomAt(e){return this._editorRuntimeOrThrow()._roomAt(e)}_overlapRoom(e){return this._editorRuntimeOrThrow()._overlapRoom(e)}_pointInRoom(e,t){return t.poly?Zt(e,t.poly):null!=t.x&&e[0]>=t.x&&e[0]<=t.x+t.w&&e[1]>=t.y&&e[1]<=t.y+t.h}_contourSelfIntersects(e){return this._editorRuntimeOrThrow()._contourSelfIntersects(e)}_canAppendRoomDraftPoint(){return this._editorRuntimeOrThrow()._canAppendRoomDraftPoint()}_markupClick(e){if(this._editorRuntime)return this._editorRuntime._markupClick(e)}_draftEndAt(e,t){return this._editorRuntimeOrThrow()._draftEndAt(e,t)}_mergeDraftEndpoint(e){return this._editorRuntimeOrThrow()._mergeDraftEndpoint(e)}_adoptDraftCms(e,t,i){return this._editorRuntimeOrThrow()._adoptDraftCms(e,t,i)}_draftSegmentsForPath(e,t,i){return this._editorRuntimeOrThrow()._draftSegmentsForPath(e,t,i)}_persistActiveDraftSegment(){return this._editorRuntimeOrThrow()._persistActiveDraftSegment()}_activeWallSourceKey(e){return this._editorRuntimeOrThrow()._activeWallSourceKey(e)}_wallGraphSources(e){return this._editorRuntimeOrThrow()._wallGraphSources(e)}_wallFaceGraph(e,t){return this._editorRuntimeOrThrow()._wallFaceGraph(e,t)}_offerWallFaces(e,t=this._path.length-2,i){return this._editorRuntimeOrThrow()._offerWallFaces(e,t,i)}_beginWallFaceBatch(e){return this._editorRuntimeOrThrow()._beginWallFaceBatch(e)}_offerExistingWallFace(e){return this._editorRuntimeOrThrow()._offerExistingWallFace(e)}_columnClick(e){return this._editorRuntimeOrThrow()._columnClick(e)}_openPhysicalDialog(e,t,i){return this._editorRuntimeOrThrow()._openPhysicalDialog(e,t,i)}_physicalDown(e,t,i){return this._editorRuntimeOrThrow()._physicalDown(e,t,i)}_clampPhysicalDelta(e,t,i){return this._editorRuntimeOrThrow()._clampPhysicalDelta(e,t,i)}_physicalMove(e){return this._editorRuntimeOrThrow()._physicalMove(e)}_physicalUp(e){return this._editorRuntimeOrThrow()._physicalUp(e)}_registerPhysicalTap(e,t,i){return this._editorRuntimeOrThrow()._registerPhysicalTap(e,t,i)}_cancelPhysicalGesture(){return this._editorRuntimeOrThrow()._cancelPhysicalGesture()}_physicalRotateDown(e,t){return this._editorRuntimeOrThrow()._physicalRotateDown(e,t)}_physicalRotateMove(e){return this._editorRuntimeOrThrow()._physicalRotateMove(e)}_physicalRotateUp(e){return this._editorRuntimeOrThrow()._physicalRotateUp(e)}_rszRooms(){return this._editorRuntimeOrThrow()._rszRooms()}_rszOpenings(){return this._editorRuntimeOrThrow()._rszOpenings()}_rszObstacles(){return this._editorRuntimeOrThrow()._rszObstacles()}_rszOptsFor(e,t){return this._editorRuntimeOrThrow()._rszOptsFor(e,t)}_rszResolution(e,t,i){return this._editorRuntimeOrThrow()._rszResolution(e,t,i)}_rszSnapshot(){return this._editorRuntimeOrThrow()._rszSnapshot()}_rszResetController(){return this._editorRuntimeOrThrow()._rszResetController()}_rszProjectPreview(e,t,i,n,r){return this._editorRuntimeOrThrow()._rszProjectPreview(e,t,i,n,r)}_rszAcceptPreview(e,t){return this._editorRuntimeOrThrow()._rszAcceptPreview(e,t)}_rszSpaceCandidateGeometry(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateGeometry(e,t)}_rszSpaceCandidateRenderable(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateRenderable(e,t)}_rszCandidateRenderable(e){return this._editorRuntimeOrThrow()._rszCandidateRenderable(e)}_rszEdgeDown(e,t,i){return this._editorRuntimeOrThrow()._rszEdgeDown(e,t,i)}_rszReasonText(e){return this._editorRuntimeOrThrow()._rszReasonText(e)}_rszDisabledActivate(e,t){return this._editorRuntimeOrThrow()._rszDisabledActivate(e,t)}_rszDisabledKey(e,t){return this._editorRuntimeOrThrow()._rszDisabledKey(e,t)}_rszMove(e){return this._editorRuntimeOrThrow()._rszMove(e)}_rszUp(e){return this._editorRuntimeOrThrow()._rszUp(e)}_rszCancelDrag(e){return this._editorRuntimeOrThrow()._rszCancelDrag(e)}_rszPointerCancel(e){return this._editorRuntimeOrThrow()._rszPointerCancel(e)}_rszEdgeLabels(e,t,i=this._resize?.rooms){return this._editorRuntimeOrThrow()._rszEdgeLabels(e,t,i)}_rszInnerSpanCms(e,t,i){return this._editorRuntimeOrThrow()._rszInnerSpanCms(e,t,i)}_renderResizeMeasurements(){return this._editorRuntimeOrThrow()._renderResizeMeasurements()}_renderResizeLayer(e){return this._editorRuntimeOrThrow()._renderResizeLayer(e)}get _openingsR(){const e=this._curSpaceCfg,t=this._spaceH,i=this._spaceModel();return i?(e?.openings||[]).flatMap(e=>{const n={...e,rx:e.x*_f,ry:e.y*t,rlen:e.length*_f};if(!e.host||"wall"===e.host.kind)return[n];const r=Zd(e,i.partitions,_f,this._cellCm,this._gridPitch);if(!r.resolved)return"plan"===this._mode?[{...n,orphanReason:r.reason||"invalid-host"}]:[];let[o,s]=r.resolved.center;const a=this._physicalDrag;return a?.moved&&"partition"===a.kind&&a.id===e.host.id&&(o+=a.delta[0],s+=a.delta[1]),[{...e,rx:o,ry:s,rlen:r.resolved.length,angle:r.resolved.angle,partitionHost:r.resolved}]}):[]}_partitionOpeningCuts(e=this._spaceModel(),t=()=>!0){if(!e)return[];return Lp(Hp(this._curSpaceCfg?.id===e.id?this._curSpaceCfg:null,e,this._cellCm,this._gridPitch,_f),t)}_roomWallOpeningInputs(e=this._openingsR,t=this._spaceModel()){if(!t)return[];const i=this._openCuts();return Bp(e,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f)}_openingFace(e,t,i){return e.partitionHost?tu(e.partitionHost,i):qs(t,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:i})}_cmToUnits(e){return e/this._cellCm*this._gridPitch}get _decorList(){const e=this._curSpaceCfg;return Array.isArray(e?.decor)?e.decor:[]}get _decorH(){return _f}_decorResolvedStyle(e){return function(e,t,i,n=rr){const r="rect"===e?.kind||"ellipse"===e?.kind,o=e,s=Dt(o?.color,n.color);return{color:s,opacity:or(o?.opacity,n.opacity),widthCm:dr(e,t,i,n.widthCm),fill:!!r&&!0===o?.fill,fillColor:Dt(o?.fill_color,o?.fill?s:n.fillColor),fillOpacity:r&&o?.fill?or(o?.fill_opacity,.25):n.fillOpacity}}(e,this._cellCm,this._gridPitch,rr)}_decorWidthUnits(e){return((e,t,i,n=rr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return cr(r,t,i);const o=Number(e?.width);return Number.isFinite(o)&&o>0?o:cr(n,t,i)})(e,this._cellCm,this._gridPitch,rr.widthCm)}_decorTextSizeCm(e){if("text"===e?.kind){const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?t:hr(20*Ei(e),this._cellCm,this._gridPitch)}return hr(20,this._cellCm,this._gridPitch)}_decorTextUnits(e){if("text"!==e.kind)return 20;const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?cr(t,this._cellCm,this._gridPitch):20*Ei(e)}_decorSmallField(e){return Math.round(100*(this._imperial?e/2.54:e))/100}_decorSmallCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(100,t)):.1}_decorTextCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(2e3,t)):.1}_decorLargeField(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_decorLargeCm(e){const t=this._imperial?30.48*e:100*e;return Number.isFinite(t)?Math.max(.1,Math.min(Da*this._cellCm,t)):.1}_angleField(e){const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(3))):"0"}_decorBoxOf(e){return"rect"!==e.kind&&"ellipse"!==e.kind&&"furniture"!==e.kind&&"image"!==e.kind?null:{x:e.x*_f,y:e.y*this._decorH,w:e.w*_f,h:e.h*this._decorH,angle:sr(e.angle)||void 0}}_decorSnapGeometry(e){return this._editorRuntimeOrThrow()._decorSnapGeometry(e)}_decorSnap(e,t="mouse",i){return this._editorRuntimeOrThrow()._decorSnap(e,t,i)}_replaceDecor(e,t){return this._editorRuntimeOrThrow()._replaceDecor(e,t)}_cancelDecorGesture(){return this._editorRuntimeOrThrow()._cancelDecorGesture()}_decorPointerDown(e){return this._editorRuntimeOrThrow()._decorPointerDown(e)}_decorCommitDraft(){return this._editorRuntimeOrThrow()._decorCommitDraft()}_decorShapeDown(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDown(e,t)}_decorMoveUpdate(e){return this._editorRuntimeOrThrow()._decorMoveUpdate(e)}_decorNudge(e,t){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag)return!1;const i=this._decorList.find(e=>e.id===this._decorSel),n=this._curSpaceCfg;if(!i||!n)return!1;const r=function(e,t,i,n,r,o){if(!([t,i,n,r,o].every(Number.isFinite)&&r>0))return null;let s;if(s="line"===e.kind?[[e.x1,e.y1],[e.x2,e.y2]]:"text"===e.kind?[[e.x,e.y]]:mr(e),!s.length||s.some(e=>!e.slice(0,2).every(Number.isFinite)))return null;const a=s.map(e=>e[0]),l=s.map(e=>e[1]),c=Math.min(...a),h=Math.max(...a),d=Math.min(...l),u=Math.max(...l),p=t/n,m=i/r,_=p>0?Math.min(p,Math.max(0,o-h)):Math.max(p,Math.min(0,-o-c)),g=m>0?Math.min(m,Math.max(0,o-u)):Math.max(m,Math.min(0,-o-d));return _||g?"line"===e.kind?{...e,x1:e.x1+_,y1:e.y1+g,x2:e.x2+_,y2:e.y2+g}:{...e,x:e.x+_,y:e.y+g}:null}(i,e,t,_f,this._decorH,Da);if(!r)return!1;const o=this._geometrySnapshot();return n.decor=this._decorList.map(e=>e.id===i.id?r:e),this._recordGeometry(this._t("history.decor_move"),o),this._saveConfig(),this.requestUpdate(),!0}_decorShapeDbl(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDbl(e,t)}_openDecorProperties(e){return this._editorRuntimeOrThrow()._openDecorProperties(e)}_decorOpenText(e){return this._editorRuntimeOrThrow()._decorOpenText(e)}_decorRememberTextSelection(e){return this._editorRuntimeOrThrow()._decorRememberTextSelection(e)}_decorInsertLiveVariable(e){return this._editorRuntimeOrThrow()._decorInsertLiveVariable(e)}_decorSaveText(){return this._editorRuntimeOrThrow()._decorSaveText()}_decorSaveShape(){return this._editorRuntimeOrThrow()._decorSaveShape()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(e=>e.id===this._decorSel)||null}_dtPivot(e){return this._editorRuntimeOrThrow()._dtPivot(e)}_dtApply(e,t){return this._editorRuntimeOrThrow()._dtApply(e,t)}_dtStart(e,t,i,n){return this._editorRuntimeOrThrow()._dtStart(e,t,i,n)}_dtMove(e){return this._editorRuntimeOrThrow()._dtMove(e)}_dtUp(){return this._editorRuntimeOrThrow()._dtUp()}_dtMeasure(){return this._editorRuntimeOrThrow()._dtMeasure()}_deleteDecor(e){return this._editorRuntimeOrThrow()._deleteDecor(e)}_decorDeleteSel(){return this._editorRuntimeOrThrow()._decorDeleteSel()}_confirmDecorErase(){return this._editorRuntimeOrThrow()._confirmDecorErase()}get _bdBase(){const e=this._curSpaceCfg;return e?.plan_url?{...$a(e.plan_aspect,_f)}:null}get _bdRect(){const e=this._curSpaceCfg;return e?.plan_url?Ca(e,_f):null}get _bdParams(){const e=this._curSpaceCfg,t=Number(e?.plan_x),i=Number(e?.plan_y),n=Number(e?.plan_scale),r=Number(e?.plan_scale_x),o=Number(e?.plan_scale_y),s=Number.isFinite(n)&&n>0?n:1;return{dx:Number.isFinite(t)?t:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(r)&&r>0?r:s,sy:Number.isFinite(o)&&o>0?o:s,angle:sr(e?.plan_angle)}}_openBackdropDialog(e){if(this._editorRuntime)return this._editorRuntime._openBackdropDialog(e)}_saveBackdropDialog(){return this._editorRuntimeOrThrow()._saveBackdropDialog()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(e,t,i,n,r){return this._editorRuntimeOrThrow()._bdApply(e,t,i,n,r)}_bdStart(e,t,i=!1){return this._editorRuntimeOrThrow()._bdStart(e,t,i)}_bdMove(e){return this._editorRuntimeOrThrow()._bdMove(e)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const e=this._bdParams;return 0!==e.dx||0!==e.dy||1!==e.sx||1!==e.sy||0!==e.angle}_bdReset(){return this._editorRuntimeOrThrow()._bdReset()}_bdUp(){return this._editorRuntimeOrThrow()._bdUp()}get _bdLive(){if(!this._bdDrag)return null;const e=this._bdRect;return e?{x:e.x+e.w/2,y:e.y+e.h/2,text:`${this._fmtLen([0,0],[e.w,0])} × ${this._fmtLen([0,0],[0,e.h])}`}:null}_renderBackdropFrame(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderBackdropFrame(e):V}_renderTextFrame(e){const t=this._dtSel,i=this._dtBox;if(!t||!i||i.id!==t.id)return V;const n=.018*Math.max(e.w,e.h),r=n/4;if("line"===t.kind){const e=[t.x1*_f,t.y1*this._decorH],i=[t.x2*_f,t.y2*this._decorH];return j` + `}_stageAspect(){const e=this._stageEl,t=this._baseVb();return e&&e.clientHeight?e.clientWidth/e.clientHeight:t[2]/t[3]}_viewOr(e){return this._view&&this._view.w?this._view:vi(e,this._stageAspect())}_roomLabelReferenceViewWidth(e){if(!this._markup)return e.w;const t=this._stageEl,i=this.renderRoot.querySelector(".editorchrome");if(!t||t.clientWidth<=0||t.clientHeight<=0)return e.w;const n=t.clientHeight+(i?.getBoundingClientRect().height||0);return n<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,n).w}_screenToVb(e,t){const i=this._stageEl,n=this._viewOr(this._baseVb()),r=i?.clientWidth||1,o=i?.clientHeight||1;return[n.x+e/r*n.w,n.y+t/o*n.h]}_clampView(e,t){const i=(e,t,i,n)=>{const r=1*Math.max(t,n),o=i-r,s=i+n-t+r;return Math.max(Math.min(o,s),Math.min(Math.max(o,s),e))};return{w:e.w,h:e.h,x:i(e.x,e.w,t.x,t.w),y:i(e.y,e.h,t.y,t.h)}}_applyView(e,t,i){this._cancelCameraTransition(!1);const n=this._baseVb(),r=vi(n,this._stageAspect()),o=Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,e)),s=r.w/o,a=r.h/o,l=this._viewOr(n),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-s/2,y:h-a/2,w:s,h:a},r);return!(this._view&&Math.abs(this._zoom-o)<1e-9&&Math.abs(this._view.x-d.x)<1e-6&&Math.abs(this._view.y-d.y)<1e-6&&Math.abs(this._view.w-d.w)<1e-6&&Math.abs(this._view.h-d.h)<1e-6)&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=o,this._view=d,this._liveVp(),!0)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const e=()=>{if(!this._booting)return;const t=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=t);const n=t-this._bootStart;n>=1200||n>=700&&i>0&&t-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(e,100)};this._bootTimer=window.setTimeout(e,100)}_bootSettled(){this._booting&&!this._bootSettling&&(this._bootSettling=!0,this._refitView(),this._bootSettleRaf=requestAnimationFrame(()=>{this._bootSettleRaf=0,this._finishBootSettled()}))}_finishBootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1,this._bootSettling=!1;const e=this._stageEl?.clientHeight??0;!this._config?.kiosk&&e>0&&this._warmPatch({hdrH:this._hdrH,stageH:e,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},lf)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._booting||this._resumeSettling||(this._modeTransitionPreparing?this._modeTransitionForceAtomic=!0:this._cancelModeTransition(!0),this._viewportInvalidAt=0,this._beginContinuityCandidate("warm-resume",!1),this._loading?this.requestUpdate():this._loadFromServer())}_refitView(){if(this._modeTransitionBusy||this._warmModeRequest)return;this._cancelCameraTransition(!1);const e=this._stageEl;if(!e||"visible"!==document.visibilityState||e.clientWidth<=0||e.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));const t=[e.clientWidth,e.clientHeight],i=this._lastValidStageSize,n=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,r=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._roomFocus?.spaceId===this._space?this._fitRoom(this._roomFocus.roomId,!1):this._view||this._applyView(this._zoom));n?this._pendingRefitSize=null:(this._pendingRefitSize=t,this._refitRaf||(this._refitRaf=requestAnimationFrame(()=>{this._refitRaf=0;const e=this._pendingRefitSize;this._pendingRefitSize=null;const t=this._stageEl;if(!e||!t||t.clientWidth<=0||t.clientHeight<=0)return;if(Math.abs(t.clientWidth-e[0])>.5||Math.abs(t.clientHeight-e[1])>.5)return void this._refitView();const i=this._lastValidStageSize;if(i&&Math.abs(i[0]-e[0])<=.5&&Math.abs(i[1]-e[1])<=.5)return;this._lastValidStageSize=e;const n=this._view;r>=zl?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._roomFocus?.spaceId!==this._space?this._applyView(this._zoom,n?n.x+n.w/2:void 0,n?n.y+n.h/2:void 0):this._fitRoom(this._roomFocus.roomId,!1)})))}_cameraTargetAt(e,t,i,n=!1){const r=this._stageEl;if(!r||r.clientWidth<=0||r.clientHeight<=0)return null;const o=vi(this._baseVb(),this._stageAspect()),s=Math.min(Mf.ZOOM_MAX,Math.max(Mf.ZOOM_MIN,i)),a=function(e,t,i,n,r,o,s){if(!Y_(e)||!Y_({zoom:t,viewBox:i})||!K_(n)||n<=0||!K_(r)||r<=0||!K_(o)||!K_(s))return null;const a=o/n,l=s/r,c=e.viewBox.x+a*e.viewBox.w,h=e.viewBox.y+l*e.viewBox.h,d=i.w/t,u=i.h/t;return{zoom:t,viewBox:{x:c-a*d,y:h-l*u,w:d,h:u}}}(n&&this._cameraTransition.target||this._cameraState(),s,o,r.clientWidth,r.clientHeight,e,t);return a?(a.viewBox=this._clampView(a.viewBox,o),{target:a,fit:o}):null}_zoomAt(e,t,i){this._clearRoomFocus(),this._cancelCameraTransition(!1);const n=this._cameraTargetAt(e,t,i);n&&("opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=n.target.zoom,this._view={...n.target.viewBox},this._liveVp())}_onWheel(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),n=e.deltaY<0?1.15:1/1.15,r=this._cameraTransition.target?.zoom??this._zoom,o=this._cameraTargetAt(e.clientX-i.left,e.clientY-i.top,r*n,!0);o&&this._startCameraTransition(o.target,o.fit,"wheel",160)}_stepZoom(e){this._clearRoomFocus(),this._prepareCameraCommand();const t=this._stageEl;if(!t)return;const i=this._cameraTransition.target?.zoom??this._zoom,n=this._cameraTargetAt(t.clientWidth/2,t.clientHeight/2,i*(e>0?1.4:1/1.4),!0);n&&this._startCameraTransition(n.target,n.fit,"button",180)}_resetZoom(e="fit"){this._clearRoomFocus(),this._prepareCameraCommand();const t=vi(this._baseVb(),this._stageAspect());this._startCameraTransition({zoom:1,viewBox:{...t}},t,e,220)}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(mf,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){this._clearRoomFocus(!0),this._cancelCameraTransition(!1);const e=this._zoomBySpace[this._space]||1;this._zoom=e;const t=this._stageEl;if(t&&t.clientHeight){const t=this._baseVb();return this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const t=this._baseVb();this._applyView(e,t[0]+t[2]/2,t[1]+t[3]/2),this.requestUpdate()})}_stagePointerDown(e){const t=e.isPrimary&&0===e.button&&"view"===this._mode?dg(e.composedPath()):null;if(this._roomPointer=t?{pointerId:e.pointerId,spaceId:this._space,roomId:t}:null,this._doubleFit.pointerDown(e,this._space,this._doubleFitEnabled),this._cancelCameraTransition(!1,!0),this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:e.clientX,y:e.clientY,id:e.pointerId},e.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._roomPointer=null,this._doubleFit.clear(),this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag||this._deviceDrag)return;if(this._markup&&e.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&e.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(e))return;this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY});const i=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:i.x,vy:i.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y);this._pinchStart={dist:t,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return!this._hasFixedFloor&&this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(e){if(this._physicalRotate?.pid!==e.pointerId)if(this._physicalDrag?.pid!==e.pointerId)if(this._dtDrag?.pid!==e.pointerId)if(this._bdDrag?.pid!==e.pointerId){if(this._decorDraft?.pid===e.pointerId){const t=this._decorDraft;let i=this._decorSnap(this._svgPoint(e),e.pointerType);if(e.shiftKey&&("rect"===t.kind||"ellipse"===t.kind)){const e=i[0]-t.a[0],n=i[1]-t.a[1],r=Math.max(Math.abs(e),Math.abs(n));i=this._snap([t.a[0]+(e<0?-r:r),t.a[1]+(n<0?-r:r)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId){if("decor"!==this._mode||"furniture"!==this._decorTool&&"image"!==this._decorTool||!this._editorRuntime||(this._notePointer(e),!this._editorRuntime._furnPointerMove(e,this._pointerModality.hoverEnabled)))if(this._pointers.has(e.pointerId)){if(this._pointers.set(e.pointerId,{x:e.clientX,y:e.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(e),this._pinchStart&&this._pointers.size>=2){this._clearRoomFocus(!0),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();const e=[...this._pointers.values()],t=Math.hypot(e[0].x-e[1].x,e[0].y-e[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),n=(e[0].x+e[1].x)/2-i.left,r=(e[0].y+e[1].y)/2-i.top;this._zoomAt(n,r,this._pinchStart.zoom*t),this._viewportGestureDirty=!0,this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const t=e.clientX-this._panStart.sx,i=e.clientY-this._panStart.sy;Math.abs(t)+Math.abs(i)>4&&(this._roomPointer=null,this._doubleFit.clear(),this._suppressClick=!0,clearTimeout(this._holdTimer),"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover()),null===this._panLock&&Math.abs(t)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(t)>1.5*Math.abs(i)?"swipe":"pan","pan"===this._panLock&&this._clearRoomFocus());const n=this._stageEl;if("pan"===this._panLock&&n){const e=this._baseVb(),r=this._viewOr(e),o=vi(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(n.clientWidth||1)*r.w,y:this._panStart.vy-i/(n.clientHeight||1)*r.h,w:r.w,h:r.h},o),this._viewportGestureDirty=!0,this._liveVp()}}}else this._markupMove(e)}else this._decorMoveUpdate(e)}else this._bdMove(e);else this._dtMove(e);else this._physicalMove(e);else this._physicalRotateMove(e)}_stagePointerLeave(e){this._editorRuntime?._cancelPointerMove("markup-hover"),"decor"===this._mode&&this._editorRuntime?._furnPointerLeave(e),this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){this._flushHa(),this._editorRuntime?._cancelPointerMove("markup-hover");const t=(i=this._roomPointer,n=e.pointerId,r=this._space,o=this._roomPointer&&"view"===this._mode?dg(e.composedPath()):null,s=this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired||this._touchSequenceMultitouch,!i||s||i.pointerId!==n||i.spaceId!==r||i.roomId!==o?null:i.roomId);var i,n,r,o,s;this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null);if(this._doubleFit.pointerUp(e,this._space,this._doubleFitEnabled,this._suppressClick||!!this._pinchStart||null!==this._panLock||this._holdFired)&&this._fitAll("double-tap"),this._kiosk){clearTimeout(this._kioskHoldTimer);const i=this._swipeStart;if(this._swipeStart=null,!t&&i&&i.id===e.pointerId){const t=e.clientX-i.x,n=e.clientY-i.y,r="pan"===this._panLock?null:function(e,t,i,n,r,o=60){if(i>1.001||n.length<2)return null;if(Math.abs(e)e.id),this._space);r&&this._slideTo(r,t<0?"left":"right")&&(this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}this._physicalDrag?.pid!==e.pointerId?this._physicalRotate?.pid!==e.pointerId?this._dtDrag?.pid!==e.pointerId?this._bdDrag?.pid!==e.pointerId?this._decorDraft?.pid!==e.pointerId?this._decorMove?.pid!==e.pointerId?this._editorRuntime?._furnPointerUp(e)||(this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._suppressClick&&setTimeout(()=>this._suppressClick=!1,0)),this._viewportGestureDirty&&0===this._pointers.size&&!t&&(this._viewportGestureDirty=!1,this.requestUpdate()),t&&this._fitRoom(t)):this._editorRuntimeOrThrow()._dmUp():this._decorCommitDraft():this._bdUp():this._dtUp():this._physicalRotateUp(e):this._physicalUp(e)}_clickRoom(e){!this._suppressClick&&e.area&&kf("/config/areas/area/"+e.area)}_roomLabelKey(e,t){"view"!==this._mode||"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),e.stopPropagation(),this._fitRoom(t))}_pointerDown(e,t){if("view"!==this._mode&&"devices"!==this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,n=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,n),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind||this._devicePositionBusy)return;if(this._deviceDrag)return void(this._deviceDrag.pointerId!==e.pointerId&&this._cancelDeviceDrag());e.preventDefault();const i=this._pos(t);this._deviceDrag={id:t.id,spaceId:t.space,displayName:t.name,pointerId:e.pointerId,source:e.currentTarget,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1,before:T_(this._layout,t.id),start:this._devicePlacementForCanvas(t,i.x,i.y)},Sf(e),this._tip=null,this._syncLiveHover()}_pointerMove(e,t){if("devices"!==this._mode)return;const i=this._deviceDrag;i&&i.id===t.id&&i.pointerId===e.pointerId&&this._editorRuntimeOrThrow()._queuePointerMove("device",()=>this._pointerMoveNow(e,t))}_pointerMoveNow(e,t){if("devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;const n=this.renderRoot.querySelector(".stage");if(!n)return;const r=this._baseVb(),o=n.getBoundingClientRect(),s=this._viewOr(r),a=(e.clientX-i.sx)/o.width*s.w,l=(e.clientY-i.sy)/o.height*s.h;Math.abs(e.clientX-i.sx)+Math.abs(e.clientY-i.sy)>3&&(i.moved=!0,clearTimeout(this._holdTimer));const c=Na(i.ox+a),h=Na(i.oy+l);this._previewDevicePlacement(t.id,this._devicePlacementForCanvas(t,c,h))}_pointerUp(e,t){if(clearTimeout(this._holdTimer),"devices"!==this._mode)return;const i=this._deviceDrag;if(!i||i.id!==t.id||i.pointerId!==e.pointerId)return;this._editorRuntimeOrThrow()._flushPointerMove("device"),this._deviceDrag=null;const n=T_(this._layout,t.id);var r,o;i.moved&&null!==n&&(r=n,o=i.start,null===r||null===o?r!==o:r.x!==o.x||r.y!==o.y||r.s!==o.s)?(this._selId=t.id,this._suppressClick=!0,window.setTimeout(()=>{this._suppressClick=!1},0),this._devicePositionBusy=!0,this.requestUpdate(),this._persistDevicePlacement(t.id,n).then(()=>{const e=this._t("history.device_move",{name:i.displayName});this._devicePositionHistory.push({name:e,before:{deviceId:i.id,spaceId:i.spaceId,placement:i.before},after:{deviceId:i.id,spaceId:i.spaceId,placement:n}})}).catch(e=>{this._previewDevicePlacement(t.id,i.before),this._showToast(this._t("toast.pos_save_failed",{err:this._errText(e)}))}).finally(()=>{this._devicePositionBusy=!1,this.requestUpdate()})):this._previewDevicePlacement(t.id,i.before)}_pointerCancel(e,t){this._deviceDrag?.id===t.id&&this._deviceDrag.pointerId===e.pointerId&&this._cancelDeviceDrag()}_showToast(e){for(const e of this.renderRoot.querySelectorAll("hp-dialog"))e.closeTransientOverlays("toast");this._toast=e,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}_syncPointerHoverTargets(){const e=this._pointerModality.hoverEnabled;for(const t of this.renderRoot.querySelectorAll(vf))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(vf)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(vf))e.toggleAttribute("data-pointer-hover",i)}_clearTransientHover(e=!1){e&&this._pointerModality.suspend(),this._tip&&(this._tip=null),this._hoverRoom&&(this._hoverRoom=null),this._syncLiveHover()}_notePointer(e){const t=this._pointerModality.modality,i=this._pointerModality.note(e);"touch"!==i&&"pen"!==i||t===i&&!this._tip&&!this._hoverRoom||this._clearTransientHover()}_guardTouchGesture(e){if(this._editorSecondary?.handleOutsideDismiss(e))return;if("click"===e.type){if(!this._suppressClick&&!this._touchSequenceMultitouch&&Date.now()>this._touchClickBlockUntil)return;return e.preventDefault(),void e.stopImmediatePropagation()}const t=e;if(this._notePointer(t),"pointercancel"===e.type||"lostpointercapture"===e.type?this._doubleFit.clear():"pointerdown"===e.type&&this._doubleFit.clearOutside(t),"touch"===t.pointerType)if("pointerdown"!==e.type){if("pointermove"===e.type){const e=this._touchContacts.get(t.pointerId);if(!e)return;const i={...e,x:t.clientX,y:t.clientY};if(this._touchContacts.set(t.pointerId,i),this._touchSequenceMultitouch&&"view"===this._mode&&!this._vacFit&&this._pinchStart&&[...this._touchContacts.values()].every(e=>e.inStage)){this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=[...this._touchContacts.values()];if(e.length>=2&&this._stageEl){const[t,i]=e,n=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),r=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-r.left,(t.y+i.y)/2-r.top,this._pinchStart.zoom*n),this._saveZoom()}}return}if("pointerup"===e.type||"pointercancel"===e.type||"lostpointercapture"===e.type){if(this._clearTransientHover(),this._touchContacts.delete(t.pointerId),this._touchSequenceMultitouch){if(this._touchClickBlockUntil=Date.now()+500,this._pointers.delete(t.pointerId),!this._vacFit&&this._pointers.size>=2){const[e,t]=[...this._pointers.values()];this._pinchStart={dist:Math.hypot(e.x-t.x,e.y-t.y),zoom:this._zoom}}else this._pinchStart=null;0===this._pointers.size&&(this._panStart=null,this._panLock=null)}0===this._touchContacts.size&&(this._touchSequenceMultitouch=!1)}}else if(this._touchContacts.set(t.pointerId,{x:t.clientX,y:t.clientY,inStage:!!t.target?.closest?.(".stage")}),this._touchContacts.size>=2){this._clearRoomFocus(!0),this._clearTransientHover(),this._touchSequenceMultitouch=!0,this._touchClickBlockUntil=Number.POSITIVE_INFINITY,clearTimeout(this._holdTimer),clearTimeout(this._kioskHoldTimer),this._swipeStart=null;const e=[...this._touchContacts.values()];if("view"===this._mode&&!this._vacFit&&e.every(e=>e.inStage)){this._pointers=new Map([...this._touchContacts].map(([e,t])=>[e,{x:t.x,y:t.y}]));const[t,i]=e;this._pinchStart={dist:Math.hypot(t.x-i.x,t.y-i.y),zoom:this._zoom},this._panStart=null,this._panLock=null}else this._vacFit&&(this._pointers.clear(),this._pinchStart=null,this._panStart=null,this._panLock=null)}}_showTip(e,t,i,n,r,o,s=!1){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||this._deviceDrag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:n,temp:r,hum:o,room:s},this._syncLiveHover()))}_roomTipEnabledForPointer(e){return"view"===this._mode&&(this._notePointer(e),!!Ki(this._settings)||(this._tip?.room&&(this._tip=null,this._syncLiveHover()),!1))}get _gridPitch(){return Fa}get _cellCm(){const e=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(e)&&e>0?e:5}_fmtLen(e,t){const i=function(e,t,i,n){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*n}(e,t,this._gridPitch,this._cellCm);return Et(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const e=this._resize?.preview;return e&&e.space===this._space?e.sp:this._serverCfg?.spaces.find(e=>e.id===this._space)}get _renderCfg(){const e=this._resize?.preview;return e&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(t=>t.id===e.space?e.sp:t)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,yf}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return Bt(e?.rooms||[]).map(e=>[e[0]*yf,e[1]*t,e[2]*yf,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(_f)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(_f,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,this._clearRoomFocus(!0),this._cancelDangerConfirm(),"view"!==this._mode&&this._setMode("view",!1),this._pendingNavMode=null,this._geometryHistory.clear(),this._activeDraftId=null,this._resumeDraftBySpace={},this._draftSegmentCms=[],this._closingWallCm=null,this._drawWallField=null,this._showHidden=!1,this._tapConfirm=null,this._vacCalConfirm=null,this._decorEraseConfirm=null,this._openingInfo=null,this._closeInfoCard(),this._rulesDialog=null,this._alignDialog=null,this._preflightClipboardFallback=null,this._backupImportDialog=null,this._backupExportDialog=null,this._supportDialog?.preview?.token&&this._editorRuntime?._discardSupportPreview(this._supportDialog.preview.token),this._supportDialog=null,this._settingsDialog=null,this._deviceInbox=null,this._deviceInboxReturn=null,this._markerDialog=null,this._openingDialog=null,this._physicalDialog=null,this._wallDialog=null,this._backdropDialog=null,this._decorShapeDialog=null,this._decorTextDialog=null,this._mergeDialog=null,this._roomDialog=!1,this._spaceDialog=null,this._importDialog=null;const e=this._warmSlot;e?.owner===this._warmGen&&(e.vp=this._warmViewportState(),e.dlg=null,e.frameFingerprint="",clearTimeout(e.evict),e.evict=0),this._saveNav()}_setMode(e,t=!0){return e!==this._mode&&(this._clearRoomFocus(!0),this._cancelDangerConfirm()),this._warmModeRequest=0,this._editorRuntime?this._editorRuntime._setMode(e,t):"view"===e?void this._editorModeRequest++:void this._requestMode(e,t)}_primeDrawWallField(){return this._editorRuntimeOrThrow()._primeDrawWallField()}get _drawWallFieldValue(){return null===this._drawWallField?uo(15,this._imperial):this._drawWallField}get _drawWallCm(){const e=(e=>{const t=String(e??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(t))return null;const i=Number(t);return Number.isFinite(i)?i:null})(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Gs:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Gs:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){return this._editorRuntimeOrThrow()._showPhysicalRange(e,t)}_draftSegmentCount(e=this._curSpaceCfg){return this._editorRuntimeOrThrow()._draftSegmentCount(e)}_mergeSpacePartitions(e,t){return this._editorRuntimeOrThrow()._mergeSpacePartitions(e,t)}_finishWallChain(){return this._editorRuntimeOrThrow()._finishWallChain()}_activateMarkupTool(e){return this._editorRuntimeOrThrow()._activateMarkupTool(e)}_limitReached(e){return this._editorRuntimeOrThrow()._limitReached(e)}_svgPoint(e){return this._editorRuntimeOrThrow()._svgPoint(e)}_snap(e){return this._editorRuntimeOrThrow()._snap(e)}_snapDrawPoint(e,t=!1){return this._editorRuntimeOrThrow()._snapDrawPoint(e,t)}_planSnapOpeningCuts(e,t){return this._editorRuntimeOrThrow()._planSnapOpeningCuts(e,t)}_planSnapGeometrySnapshot(){return this._editorRuntimeOrThrow()._planSnapGeometrySnapshot()}_hiddenWallDiagnosticSnapshot(){return this._editorRuntimeOrThrow()._hiddenWallDiagnosticSnapshot()}_planStructuralGeometrySnapshot(){return this._editorRuntimeOrThrow()._planStructuralGeometrySnapshot()}_planSnapContextKey(e){return this._editorRuntimeOrThrow()._planSnapContextKey(e)}_resolvePlanDrawPoint(e,t){return this._editorRuntimeOrThrow()._resolvePlanDrawPoint(e,t)}get _activePlanSnapCandidate(){if(!this._markup||"draw"!==this._tool)return null;const e=this._planSnapHover;if(!e)return null;const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.candidate:null}get _activePlanSnapConflicts(){if(!this._markup||"draw"!==this._tool)return[];const e=this._planSnapHover;if(!e)return[];const t=this._planSnapGeometrySnapshot();return e.contextKey===this._planSnapContextKey(t.key)?e.conflicts:[]}_clearPlanSnapHover(e=!0){return this._editorRuntime?this._editorRuntimeOrThrow()._clearPlanSnapHover(e):(this._planSnapHover=null,void(e&&(this._cursorPt=null)))}_samePt(e,t){return Xt(e,t)}_dropLegacySegments(){return this._editorRuntimeOrThrow()._dropLegacySegments()}_rollbackRejectedPhysicalWrites(e){return this._editorRuntimeOrThrow()._rollbackRejectedPhysicalWrites(e)}async _reloadRejectedPhysicalWrite(){return this._editorRuntimeOrThrow()._reloadRejectedPhysicalWrite()}get _cfgWriting(){return this._writesPending>0}async _sendConfigCandidate(e){const t=to(e),i=await this.hass.callWS({type:"houseplan/config/set",config:t,expected_rev:this._cfgRev});this._cfgRev=i?.rev??this._cfgRev+1}_writeConfig(){if(this._editorRuntime)return this._editorRuntime._writeConfig();this._writesPending++,this._writeChain=$_(this._writeChain,async()=>{if(!this._serverCfg)return;const e=to(this._serverCfg),t=El(e);t!==El(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t,await this._sendConfigCandidate(e)});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){return this._editorRuntimeOrThrow()._geometrySnapshotFromConfig(e,t)}_geometrySnapshot(e=this._space){return this._editorRuntimeOrThrow()._geometrySnapshot(e)}_recordGeometry(e,t){return this._editorRuntimeOrThrow()._recordGeometry(e,t)}_restoreGeometryStateInConfig(e,t,i=!1){return this._editorRuntimeOrThrow()._restoreGeometryStateInConfig(e,t,i)}_restoreGeometryStateLocal(e){return this._editorRuntimeOrThrow()._restoreGeometryStateLocal(e)}_wallModelBlockerLabel(e){return this._editorRuntimeOrThrow()._wallModelBlockerLabel(e)}_hasLegacyZeroWallFields(e=this._serverCfg){return this._editorRuntimeOrThrow()._hasLegacyZeroWallFields(e)}_showWallModelMigrationBlocked(e){return this._editorRuntimeOrThrow()._showWallModelMigrationBlocked(e)}_limitSegmentsOf(e){return this._editorRuntimeOrThrow()._limitSegmentsOf(e)}_junctionLimitViolations(e,t,i,n){return this._editorRuntimeOrThrow()._junctionLimitViolations(e,t,i,n)}_junctionLimitLabel(e){return this._editorRuntimeOrThrow()._junctionLimitLabel(e)}_junctionLimitsIntroduced(e,t,i){return this._editorRuntimeOrThrow()._junctionLimitsIntroduced(e,t,i)}_commitPhysicalGeometry(e,t,i=[]){return this._editorRuntimeOrThrow()._commitPhysicalGeometry(e,t,i)}_clearGeometryGesture(){return this._editorRuntime?this._editorRuntimeOrThrow()._clearGeometryGesture():(this._path=[],this._planSnapHover=null,this._cursorPt=null,this._openingHoverCandidate=null,this._openingJambBlockCm=null,this._openingPreset=null,this._openingRebindId=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._wallFaceBatch=null,this._wallRepairDiagnostic=null,this._roomDeleteDialog=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._resize?.reset(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,void(this._bdDrag=null))}_stagePointerCancel(e){if(this._flushHa(),this._editorRuntime?._cancelPointerMove("markup-hover"),this._roomPointer?.pointerId===e.pointerId&&(this._roomPointer=null),this._doubleFit.clear(),this._editorRuntime)return this._editorRuntime._stagePointerCancel(e);this._pointers.delete(e.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,this._swipeStart=null),this._viewportGestureDirty&&0===this._pointers.size&&(this._viewportGestureDirty=!1,this.requestUpdate())}_applyGeometryState(e,t=!1){return this._editorRuntimeOrThrow()._applyGeometryState(e,t)}_roomAt(e){return this._editorRuntimeOrThrow()._roomAt(e)}_overlapRoom(e){return this._editorRuntimeOrThrow()._overlapRoom(e)}_pointInRoom(e,t){return t.poly?Zt(e,t.poly):null!=t.x&&e[0]>=t.x&&e[0]<=t.x+t.w&&e[1]>=t.y&&e[1]<=t.y+t.h}_contourSelfIntersects(e){return this._editorRuntimeOrThrow()._contourSelfIntersects(e)}_canAppendRoomDraftPoint(){return this._editorRuntimeOrThrow()._canAppendRoomDraftPoint()}_markupClick(e){if(this._editorRuntime)return this._editorRuntime._markupClick(e)}_draftEndAt(e,t){return this._editorRuntimeOrThrow()._draftEndAt(e,t)}_mergeDraftEndpoint(e){return this._editorRuntimeOrThrow()._mergeDraftEndpoint(e)}_adoptDraftCms(e,t,i){return this._editorRuntimeOrThrow()._adoptDraftCms(e,t,i)}_draftSegmentsForPath(e,t,i){return this._editorRuntimeOrThrow()._draftSegmentsForPath(e,t,i)}_persistActiveDraftSegment(){return this._editorRuntimeOrThrow()._persistActiveDraftSegment()}_activeWallSourceKey(e){return this._editorRuntimeOrThrow()._activeWallSourceKey(e)}_wallGraphSources(e){return this._editorRuntimeOrThrow()._wallGraphSources(e)}_wallFaceGraph(e,t){return this._editorRuntimeOrThrow()._wallFaceGraph(e,t)}_offerWallFaces(e,t=this._path.length-2,i){return this._editorRuntimeOrThrow()._offerWallFaces(e,t,i)}_beginWallFaceBatch(e){return this._editorRuntimeOrThrow()._beginWallFaceBatch(e)}_offerExistingWallFace(e){return this._editorRuntimeOrThrow()._offerExistingWallFace(e)}_columnClick(e){return this._editorRuntimeOrThrow()._columnClick(e)}_openPhysicalDialog(e,t,i){return this._editorRuntimeOrThrow()._openPhysicalDialog(e,t,i)}_physicalDown(e,t,i){return this._editorRuntimeOrThrow()._physicalDown(e,t,i)}_clampPhysicalDelta(e,t,i){return this._editorRuntimeOrThrow()._clampPhysicalDelta(e,t,i)}_physicalMove(e){return this._editorRuntimeOrThrow()._physicalMove(e)}_physicalUp(e){return this._editorRuntimeOrThrow()._physicalUp(e)}_registerPhysicalTap(e,t,i){return this._editorRuntimeOrThrow()._registerPhysicalTap(e,t,i)}_cancelPhysicalGesture(){return this._editorRuntimeOrThrow()._cancelPhysicalGesture()}_physicalRotateDown(e,t){return this._editorRuntimeOrThrow()._physicalRotateDown(e,t)}_physicalRotateMove(e){return this._editorRuntimeOrThrow()._physicalRotateMove(e)}_physicalRotateUp(e){return this._editorRuntimeOrThrow()._physicalRotateUp(e)}_rszRooms(){return this._editorRuntimeOrThrow()._rszRooms()}_rszOpenings(){return this._editorRuntimeOrThrow()._rszOpenings()}_rszObstacles(){return this._editorRuntimeOrThrow()._rszObstacles()}_rszOptsFor(e,t){return this._editorRuntimeOrThrow()._rszOptsFor(e,t)}_rszResolution(e,t,i){return this._editorRuntimeOrThrow()._rszResolution(e,t,i)}_rszSnapshot(){return this._editorRuntimeOrThrow()._rszSnapshot()}_rszResetController(){return this._editorRuntimeOrThrow()._rszResetController()}_rszProjectPreview(e,t,i,n,r){return this._editorRuntimeOrThrow()._rszProjectPreview(e,t,i,n,r)}_rszAcceptPreview(e,t){return this._editorRuntimeOrThrow()._rszAcceptPreview(e,t)}_rszSpaceCandidateGeometry(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateGeometry(e,t)}_rszSpaceCandidateRenderable(e,t){return this._editorRuntimeOrThrow()._rszSpaceCandidateRenderable(e,t)}_rszCandidateRenderable(e){return this._editorRuntimeOrThrow()._rszCandidateRenderable(e)}_rszEdgeDown(e,t,i){return this._editorRuntimeOrThrow()._rszEdgeDown(e,t,i)}_rszReasonText(e){return this._editorRuntimeOrThrow()._rszReasonText(e)}_rszDisabledActivate(e,t){return this._editorRuntimeOrThrow()._rszDisabledActivate(e,t)}_rszDisabledKey(e,t){return this._editorRuntimeOrThrow()._rszDisabledKey(e,t)}_rszMove(e){return this._editorRuntimeOrThrow()._rszMove(e)}_rszUp(e){return this._editorRuntimeOrThrow()._rszUp(e)}_rszCancelDrag(e){return this._editorRuntimeOrThrow()._rszCancelDrag(e)}_rszPointerCancel(e){return this._editorRuntimeOrThrow()._rszPointerCancel(e)}_rszEdgeLabels(e,t,i=this._resize?.rooms){return this._editorRuntimeOrThrow()._rszEdgeLabels(e,t,i)}_rszInnerSpanCms(e,t,i){return this._editorRuntimeOrThrow()._rszInnerSpanCms(e,t,i)}_renderResizeMeasurements(){return this._editorRuntimeOrThrow()._renderResizeMeasurements()}_renderResizeLayer(e,t){return this._editorRuntimeOrThrow()._renderResizeLayer(e,t)}get _openingsR(){const e=this._curSpaceCfg,t=this._spaceH,i=this._spaceModel();return i?(e?.openings||[]).flatMap(e=>{const n={...e,rx:e.x*yf,ry:e.y*t,rlen:e.length*yf};if(!e.host||"wall"===e.host.kind)return[n];const r=Qd(e,i.partitions,yf,this._cellCm,this._gridPitch);if(!r.resolved)return"plan"===this._mode?[{...n,orphanReason:r.reason||"invalid-host"}]:[];let[o,s]=r.resolved.center;const a=this._physicalDrag;return a?.moved&&"partition"===a.kind&&a.id===e.host.id&&(o+=a.delta[0],s+=a.delta[1]),[{...e,rx:o,ry:s,rlen:r.resolved.length,angle:r.resolved.angle,partitionHost:r.resolved}]}):[]}_partitionOpeningCuts(e=this._spaceModel(),t=()=>!0){if(!e)return[];return qp(Bp(this._curSpaceCfg?.id===e.id?this._curSpaceCfg:null,e,this._cellCm,this._gridPitch,yf),t)}_roomWallOpeningInputs(e=this._openingsR,t=this._spaceModel()){if(!t)return[];const i=this._openCuts();return Wp(e,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf)}_openingFace(e,t,i){return e.partitionHost?nu(e.partitionHost,i):Ws(t,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:i})}_cmToUnits(e){return e/this._cellCm*this._gridPitch}get _decorList(){const e=this._curSpaceCfg;return Array.isArray(e?.decor)?e.decor:[]}get _decorH(){return yf}_decorResolvedStyle(e){return function(e,t,i,n=rr){const r="rect"===e?.kind||"ellipse"===e?.kind,o=e,s=Dt(o?.color,n.color);return{color:s,opacity:or(o?.opacity,n.opacity),widthCm:dr(e,t,i,n.widthCm),fill:!!r&&!0===o?.fill,fillColor:Dt(o?.fill_color,o?.fill?s:n.fillColor),fillOpacity:r&&o?.fill?or(o?.fill_opacity,.25):n.fillOpacity}}(e,this._cellCm,this._gridPitch,rr)}_decorWidthUnits(e){return((e,t,i,n=rr.widthCm)=>{const r=Number(e?.width_cm);if(Number.isFinite(r)&&r>0)return cr(r,t,i);const o=Number(e?.width);return Number.isFinite(o)&&o>0?o:cr(n,t,i)})(e,this._cellCm,this._gridPitch,rr.widthCm)}_decorTextSizeCm(e){if("text"===e?.kind){const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?t:hr(20*Ei(e),this._cellCm,this._gridPitch)}return hr(20,this._cellCm,this._gridPitch)}_decorTextUnits(e){if("text"!==e.kind)return 20;const t=Number(e.size_cm);return Number.isFinite(t)&&t>0?cr(t,this._cellCm,this._gridPitch):20*Ei(e)}_decorSmallField(e){return Math.round(100*(this._imperial?e/2.54:e))/100}_decorSmallCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(100,t)):.1}_decorTextCm(e){const t=this._imperial?2.54*e:e;return Number.isFinite(t)?Math.max(.1,Math.min(2e3,t)):.1}_decorLargeField(e){return Math.round(100*(this._imperial?e/30.48:e/100))/100}_decorLargeCm(e){const t=this._imperial?30.48*e:100*e;return Number.isFinite(t)?Math.max(.1,Math.min(za*this._cellCm,t)):.1}_angleField(e){const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(3))):"0"}_decorBoxOf(e){return"rect"!==e.kind&&"ellipse"!==e.kind&&"furniture"!==e.kind&&"image"!==e.kind?null:{x:e.x*yf,y:e.y*this._decorH,w:e.w*yf,h:e.h*this._decorH,angle:sr(e.angle)||void 0}}_decorSnapGeometry(e){return this._editorRuntimeOrThrow()._decorSnapGeometry(e)}_decorSnap(e,t="mouse",i){return this._editorRuntimeOrThrow()._decorSnap(e,t,i)}_replaceDecor(e,t){return this._editorRuntimeOrThrow()._replaceDecor(e,t)}_cancelDecorGesture(){return this._editorRuntimeOrThrow()._cancelDecorGesture()}_decorPointerDown(e){return this._editorRuntimeOrThrow()._decorPointerDown(e)}_decorCommitDraft(){return this._editorRuntimeOrThrow()._decorCommitDraft()}_decorShapeDown(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDown(e,t)}_decorMoveUpdate(e){this._editorRuntimeOrThrow()._queuePointerMove("decor-move",()=>this._editorRuntimeOrThrow()._decorMoveUpdate(e))}_decorNudge(e,t){if("decor"!==this._mode||"select"!==this._decorTool||!this._decorSel||this._decorDraft||this._decorMove||this._dtDrag||this._bdDrag)return!1;const i=this._decorList.find(e=>e.id===this._decorSel),n=this._curSpaceCfg;if(!i||!n)return!1;const r=function(e,t,i,n,r,o){if(!([t,i,n,r,o].every(Number.isFinite)&&r>0))return null;let s;if(s="line"===e.kind?[[e.x1,e.y1],[e.x2,e.y2]]:"text"===e.kind?[[e.x,e.y]]:mr(e),!s.length||s.some(e=>!e.slice(0,2).every(Number.isFinite)))return null;const a=s.map(e=>e[0]),l=s.map(e=>e[1]),c=Math.min(...a),h=Math.max(...a),d=Math.min(...l),u=Math.max(...l),p=t/n,m=i/r,_=p>0?Math.min(p,Math.max(0,o-h)):Math.max(p,Math.min(0,-o-c)),g=m>0?Math.min(m,Math.max(0,o-u)):Math.max(m,Math.min(0,-o-d));return _||g?"line"===e.kind?{...e,x1:e.x1+_,y1:e.y1+g,x2:e.x2+_,y2:e.y2+g}:{...e,x:e.x+_,y:e.y+g}:null}(i,e,t,yf,this._decorH,za);if(!r)return!1;const o=this._geometrySnapshot();return n.decor=this._decorList.map(e=>e.id===i.id?r:e),this._recordGeometry(this._t("history.decor_move"),o),this._saveConfig(),this.requestUpdate(),!0}_decorShapeDbl(e,t){if(this._editorRuntime)return this._editorRuntime._decorShapeDbl(e,t)}_openDecorProperties(e){return this._editorRuntimeOrThrow()._openDecorProperties(e)}_decorOpenText(e){return this._editorRuntimeOrThrow()._decorOpenText(e)}_decorRememberTextSelection(e){return this._editorRuntimeOrThrow()._decorRememberTextSelection(e)}_decorInsertLiveVariable(e){return this._editorRuntimeOrThrow()._decorInsertLiveVariable(e)}_decorSaveText(){return this._editorRuntimeOrThrow()._decorSaveText()}_decorSaveShape(){return this._editorRuntimeOrThrow()._decorSaveShape()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(e=>e.id===this._decorSel)||null}_dtPivot(e){return this._editorRuntimeOrThrow()._dtPivot(e)}_dtApply(e,t){return this._editorRuntimeOrThrow()._dtApply(e,t)}_dtStart(e,t,i,n){return this._editorRuntimeOrThrow()._dtStart(e,t,i,n)}_dtMove(e){this._editorRuntimeOrThrow()._queuePointerMove("decor-transform",()=>this._editorRuntimeOrThrow()._dtMove(e))}_dtUp(){return this._editorRuntimeOrThrow()._flushPointerMove("decor-transform"),this._editorRuntimeOrThrow()._dtUp()}_dtMeasure(){return this._editorRuntimeOrThrow()._dtMeasure()}_deleteDecor(e){return this._editorRuntimeOrThrow()._deleteDecor(e)}_decorDeleteSel(){return this._editorRuntimeOrThrow()._decorDeleteSel()}_confirmDecorErase(){return this._editorRuntimeOrThrow()._confirmDecorErase()}get _bdBase(){const e=this._curSpaceCfg;return e?.plan_url?{...$a(e.plan_aspect,yf)}:null}get _bdRect(){const e=this._curSpaceCfg;return e?.plan_url?Ra(e,yf):null}get _bdParams(){const e=this._curSpaceCfg,t=Number(e?.plan_x),i=Number(e?.plan_y),n=Number(e?.plan_scale),r=Number(e?.plan_scale_x),o=Number(e?.plan_scale_y),s=Number.isFinite(n)&&n>0?n:1;return{dx:Number.isFinite(t)?t:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(r)&&r>0?r:s,sy:Number.isFinite(o)&&o>0?o:s,angle:sr(e?.plan_angle)}}_openBackdropDialog(e){if(this._editorRuntime)return this._editorRuntime._openBackdropDialog(e)}_saveBackdropDialog(){return this._editorRuntimeOrThrow()._saveBackdropDialog()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(e,t,i,n,r){return this._editorRuntimeOrThrow()._bdApply(e,t,i,n,r)}_bdStart(e,t,i=!1){return this._editorRuntimeOrThrow()._bdStart(e,t,i)}_bdMove(e){return this._editorRuntimeOrThrow()._bdMove(e)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const e=this._bdParams;return 0!==e.dx||0!==e.dy||1!==e.sx||1!==e.sy||0!==e.angle}_bdReset(){return this._editorRuntimeOrThrow()._bdReset()}_bdUp(){return this._editorRuntimeOrThrow()._bdUp()}get _bdLive(){if(!this._bdDrag)return null;const e=this._bdRect;return e?{x:e.x+e.w/2,y:e.y+e.h/2,text:`${this._fmtLen([0,0],[e.w,0])} × ${this._fmtLen([0,0],[0,e.h])}`}:null}_renderBackdropFrame(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderBackdropFrame(e):V}_renderTextFrame(e){const t=this._dtSel;if(!t)return V;if("text"===t.kind&&this._dtBox?.id!==t.id)return V;const i="text"===t.kind?this._dtBox:this._decorBoxOf(t);if("line"!==t.kind&&!i)return V;const n=.018*Math.max(e.w,e.h),r=n/4;if("line"===t.kind){const e=[t.x1*yf,t.y1*this._decorH],i=[t.x2*yf,t.y2*this._decorH];return j` ${[e,i].map((e,t)=>j`this._dtStart(e,"scale",void 0,t)}> `)} - `}const[o,s]=this._dtPivot(t),a=Number(t.angle)||0,l=[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]],c=2.2*n,h="furniture"===t.kind||"image"===t.kind,d=h?[[0,-1,"ns"],[1,0,"ew"],[0,1,"ns"],[-1,0,"ew"]]:[];return j` + `}if(!i)return V;const[o,s]=this._dtPivot(t),a=Number(t.angle)||0,l=[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]],c=2.2*n,h="furniture"===t.kind||"image"===t.kind,d=h?[[0,-1,"ns"],[1,0,"ew"],[0,1,"ns"],[-1,0,"ew"]]:[];return j` this._dtStart(e,"rotate")}> - ${wf.map(e=>"edges"===e?d.map(([e,t,o])=>{const s=e<0?i.x:e>0?i.x+i.w:i.x+i.w/2,a=t<0?i.y:t>0?i.y+i.h:i.y+i.h/2;return j`"edges"===e?d.map(([e,t,o])=>{const s=e<0?i.x:e>0?i.x+i.w:i.x+i.w/2,a=t<0?i.y:t>0?i.y+i.h:i.y+i.h/2;return j`this._dtStart(i,"scale",[e,t])}> `}):l.map(([e,t,o])=>j`this._dtStart(i,"scale",[e,t])}>`))} - `}_renderDecorLayer(){const e=_f,t=this._decorH,i="decor"===this._mode,n=i&&"erase"===this._decorTool,r=this._stageEl,o=this._viewOr(this._baseVb()),s="iso"===this._renderProjection?1:function(e,t,i,n){const r=Number(e),o=Number(t),s=Number(i),a=Number(n);if(![r,o,s,a].every(e=>Number.isFinite(e)&&e>0))return 1;const l=Math.min(r/s,o/a);return Number.isFinite(l)&&l>0?l:1}(r?.clientWidth,r?.clientHeight,o.w,o.h),a=this._decorList.map(r=>{const o="dshape"+(i&&this._decorSel===r.id?" dsel":""),a=this._decorResolvedStyle(r),l=this._decorWidthUnits(r),c=e=>this._decorShapeDown(e,r),h=e=>this._decorShapeDbl(e,r);if("line"===r.kind)return j``}_renderDecorLayer(e){const t=yf,i=this._decorH,n="decor"===this._mode,r=n&&"erase"===this._decorTool,o=this._stageEl,s=this._viewOr(this._baseVb()),a="iso"===this._renderProjection?1:function(e,t,i,n){const r=Number(e),o=Number(t),s=Number(i),a=Number(n);if(![r,o,s,a].every(e=>Number.isFinite(e)&&e>0))return 1;const l=Math.min(r/s,o/a);return Number.isFinite(l)&&l>0?l:1}(o?.clientWidth,o?.clientHeight,s.w,s.h),l=this._decorList.filter(t=>void 0===e||t.id===e).map(e=>{const o="dshape"+(n&&this._decorSel===e.id?" dsel":""),s=this._decorResolvedStyle(e),l=this._decorWidthUnits(e),c=t=>this._decorShapeDown(t,e),h=t=>this._decorShapeDbl(t,e);if("line"===e.kind)return j` - ${i&&"select"===this._decorTool?j``:V} - ${n?j``:V}`;if("rect"===r.kind){const i=(r.x+r.w/2)*e,s=(r.y+r.h/2)*t,d=sr(r.angle);return j``:V}`;if("rect"===e.kind){const n=(e.x+e.w/2)*t,a=(e.y+e.h/2)*i,d=sr(e.angle);return j` - ${n?j``:V}`}if("ellipse"===r.kind){const i=(r.x+r.w/2)*e,s=(r.y+r.h/2)*t,d=sr(r.angle);return j``:V}`}if("ellipse"===e.kind){const n=(e.x+e.w/2)*t,a=(e.y+e.h/2)*i,d=sr(e.angle);return j` - ${n?j``:V}`}if("image"===r.kind){const s=rm(r,e,t);if(!s)return V;const[a,l,d,u,p,m]=s,_=this._decorAssets.get(r.asset_id);if(!_)return i&&this._editorRuntime?this._editorRuntime._renderMissingDecorImage(r,o,m,a,l,d,u,c,h):V;const g=this._display(_.url);return g?j``:V}`}if("image"===e.kind){const s=sm(e,t,i);if(!s)return V;const[a,l,d,u,p,m]=s,_=this._decorAssets.get(e.asset_id);if(!_)return n&&this._editorRuntime?this._editorRuntime._renderMissingDecorImage(e,o,m,a,l,d,u,c,h):V;const g=this._display(_.url);return g?j`this._signer.markLoaded(this._renderPlanHass,_.url,g)} @pointerdown=${c} @dblclick=${h}> - ${n?j``:V}`:V}if("furniture"===r.kind){const d=kl(r.symbol);if(!d)return V;const u=Cl(r,e,t,d.viewW,d.viewH),p=xl(l,s),m=xl(l+cr(20,this._cellCm,this._gridPitch),s);return j``:V}`:V}if("furniture"===e.kind){const d=xl(e.symbol);if(!d)return V;const u=Rl(e,t,i,d.viewW,d.viewH),p=Sl(l,a),m=Sl(l+cr(20,this._cellCm,this._gridPitch),a);return j` - ${i&&"select"===this._decorTool?j``:V} - ${n?j``:V}`}if("text"===r.kind){const i=this._decorTextUnits(r),n=this._renderDeviceSnapshot?.facts.get(`decor:${this._space}:${r.id}`),s=function(e){return String(e??"").replace(/\r\n?/g,"\n").split("\n")}("string"==typeof n?n:Oi(r.text,r,this._renderPlanHass,e=>this._renderEntityAvailable(e))),l=r.x*e,d=r.y*t,u=Number(r.angle)||0,p=d-(s.length-1)*i*1.2/2;return j`${s.map((e,t)=>j`${e}`)}`}return V});let l=V;const c=this._decorDraft;if(c){const e=this._decorStyle,t=cr(e.widthCm,this._cellCm,this._gridPitch);if("line"===c.kind)l=j``;else{const i=Math.min(c.a[0],c.b[0]),n=Math.min(c.a[1],c.b[1]),r=Math.abs(c.b[0]-c.a[0]),o=Math.abs(c.b[1]-c.a[1]);l="rect"===c.kind?j``:V}`}if("text"===e.kind){const n=this._decorTextUnits(e),r=this._renderDeviceSnapshot?.facts.get(`decor:${this._space}:${e.id}`),a=function(e){return String(e??"").replace(/\r\n?/g,"\n").split("\n")}("string"==typeof r?r:Oi(e.text,e,this._renderPlanHass,e=>this._renderEntityAvailable(e))),l=e.x*t,d=e.y*i,u=Number(e.angle)||0,p=d-(a.length-1)*n*1.2/2;return j`${a.map((e,t)=>j`${e}`)}`}return V});let c=V;const h=this._decorDraft;if(h){const e=this._decorStyle,t=cr(e.widthCm,this._cellCm,this._gridPitch);if("line"===h.kind)c=j``;else{const i=Math.min(h.a[0],h.b[0]),n=Math.min(h.a[1],h.b[1]),r=Math.abs(h.b[0]-h.a[0]),o=Math.abs(h.b[1]-h.a[1]);c="rect"===h.kind?j``:j``}}return j`${a}${l}${this._editorRuntime?._renderFurniturePlacementPreview(s)??V}${this._editorRuntime?._renderDecorImagePlacementPreview()??V}`}get _editorToolbarGroups(){return"plan"!==this._mode?[]:[{id:"opening",label:this._t("markup.opening"),icon:"mdi:door",activeItemId:"opening"===this._tool?this._openingPreset?.type:void 0,items:[{id:"window",label:this._t("opening.window"),icon:"mdi:window-closed-variant",role:"tool",invoke:()=>this._activateOpeningPlacement("window")},{id:"door",label:this._t("opening.door"),icon:"mdi:door-open",role:"tool",invoke:()=>this._activateOpeningPlacement("door")},{id:"passage",label:this._t("opening.passage"),icon:"mdi:arch",role:"tool",invoke:()=>this._activateOpeningPlacement("passage")},{id:"gate",label:this._t("opening.gate"),icon:"mdi:gate",role:"tool",invoke:()=>this._activateOpeningPlacement("gate")}]}]}_renderEditorGroupLauncher(e){return this._editorRuntimeOrThrow()._renderEditorGroupLauncher(e)}get _editorSecondaryContextId(){const e=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,t=this._editorSecondary?.activeGroup(this._editorToolbarGroups);if(t)return`${e}:group:${t.id}:${this._editorSecondary?.groupGeneration}`;if("plan"===this._mode){const t=this._physicalSel;return t?`${e}:selection:${t.kind}:${t.id}:${t.segment??""}`:`${e}:tool:${this._tool}:${this._path.length}`}return"decor"===this._mode?"furniture"===this._decorTool?`${e}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"image"===this._decorTool?`${e}:palette:image:${this._decorImagePalette?.asset_id||"none"}`:"select"===this._decorTool&&this._decorSel?`${e}:selection:decor:${this._decorSel}`:`${e}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${e}:none`}_runEditorContext(e,t){return this._editorRuntimeOrThrow()._runEditorContext(e,t)}_renderEditorGroupModel(e){return this._editorRuntimeOrThrow()._renderEditorGroupModel(e)}_renderDrawWallControl(){return this._editorRuntimeOrThrow()._renderDrawWallControl()}_renderPlanSecondary(){return this._editorRuntimeOrThrow()._renderPlanSecondary()}_renderDecorSecondary(){return this._editorRuntimeOrThrow()._renderDecorSecondary()}_withBackdropReset(e){return this._editorRuntimeOrThrow()._withBackdropReset(e)}get _editorSecondaryDialogBlocked(){return!!(this._dangerConfirm||this._tapConfirm||this._vacCalConfirm||this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._openingInfo||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog||this._markerDialog||this._deviceInbox||this._infoCard||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._backupExportDialog||this._backupImportDialog||this._wallDialog)}_renderEditorSecondary(){return this._editorRuntimeOrThrow()._renderEditorSecondary()}_renderDecorBar(){return this._editorRuntimeOrThrow()._renderDecorBar()}_renderDecorEraseConfirm(){return this._editorRuntimeOrThrow()._renderDecorEraseConfirm()}_renderDecorTextDialog(){return this._editorRuntimeOrThrow()._renderDecorTextDialog()}_renderDecorShapeDialog(){return this._editorRuntimeOrThrow()._renderDecorShapeDialog()}_renderBackdropDialog(){return this._editorRuntimeOrThrow()._renderBackdropDialog()}_cssPxToRender(e){return this._editorRuntimeOrThrow()._cssPxToRender(e)}_renderZeroWalls(e){if(e&&!e.showBorders&&!this._editing)return j``;const t=this._zeroWalls();if(!t.lines.length)return j``;const i=e?.color||"var(--hp-muted)";return j``}}return j`${l}${c}${this._editorRuntime?._renderFurniturePlacementPreview(a)??V}${this._editorRuntime?._renderDecorImagePlacementPreview()??V}`}get _editorToolbarGroups(){return"plan"!==this._mode?[]:[{id:"opening",label:this._t("markup.opening"),icon:"mdi:door",activeItemId:"opening"===this._tool?this._openingPreset?.type:void 0,items:[{id:"window",label:this._t("opening.window"),icon:"mdi:window-closed-variant",role:"tool",invoke:()=>this._activateOpeningPlacement("window")},{id:"door",label:this._t("opening.door"),icon:"mdi:door-open",role:"tool",invoke:()=>this._activateOpeningPlacement("door")},{id:"passage",label:this._t("opening.passage"),icon:"mdi:arch",role:"tool",invoke:()=>this._activateOpeningPlacement("passage")},{id:"gate",label:this._t("opening.gate"),icon:"mdi:gate",role:"tool",invoke:()=>this._activateOpeningPlacement("gate")}]}]}_renderEditorGroupLauncher(e){return this._editorRuntimeOrThrow()._renderEditorGroupLauncher(e)}get _editorSecondaryContextId(){const e=`editor:${this._mode}:${this._space}:${this._cfgEpoch}`,t=this._editorSecondary?.activeGroup(this._editorToolbarGroups);if(t)return`${e}:group:${t.id}:${this._editorSecondary?.groupGeneration}`;if("plan"===this._mode){const t=this._physicalSel;return t?`${e}:selection:${t.kind}:${t.id}:${t.segment??""}`:`${e}:tool:${this._tool}:${this._path.length}`}return"decor"===this._mode?"furniture"===this._decorTool?`${e}:palette:furniture:${this._furnPalette?.symbol||"none"}`:"image"===this._decorTool?`${e}:palette:image:${this._decorImagePalette?.asset_id||"none"}`:"select"===this._decorTool&&this._decorSel?`${e}:selection:decor:${this._decorSel}`:`${e}:tool:${this._decorTool}:${this._bdMoved?1:0}:${this._bdDrag?1:0}`:`${e}:none`}_runEditorContext(e,t){return this._editorRuntimeOrThrow()._runEditorContext(e,t)}_renderEditorGroupModel(e){return this._editorRuntimeOrThrow()._renderEditorGroupModel(e)}_renderDrawWallControl(){return this._editorRuntimeOrThrow()._renderDrawWallControl()}_renderPlanSecondary(){return this._editorRuntimeOrThrow()._renderPlanSecondary()}_renderDecorSecondary(){return this._editorRuntimeOrThrow()._renderDecorSecondary()}_withBackdropReset(e){return this._editorRuntimeOrThrow()._withBackdropReset(e)}get _editorSecondaryDialogBlocked(){return!!(this._dangerConfirm||this._tapConfirm||this._vacCalConfirm||this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._openingInfo||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog||this._markerDialog||this._deviceInbox||this._infoCard||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._importDialog||this._kioskDialog||this._backupExportDialog||this._backupImportDialog||this._wallDialog)}_renderEditorSecondary(){return this._editorRuntimeOrThrow()._renderEditorSecondary()}_renderDecorBar(){return this._editorRuntimeOrThrow()._renderDecorBar()}_renderDecorEraseConfirm(){return this._editorRuntimeOrThrow()._renderDecorEraseConfirm()}_renderDecorTextDialog(){return this._editorRuntimeOrThrow()._renderDecorTextDialog()}_renderDecorShapeDialog(){return this._editorRuntimeOrThrow()._renderDecorShapeDialog()}_renderBackdropDialog(){return this._editorRuntimeOrThrow()._renderBackdropDialog()}_cssPxToRender(e){return this._editorRuntimeOrThrow()._cssPxToRender(e)}_renderZeroWalls(e){if(e&&!e.showBorders&&!this._editing)return j``;const t=this._zeroWalls();if(!t.lines.length)return j``;const i=e?.color||"var(--hp-muted)";return j` ${t.lines.map(e=>j``)} - `}_zeroWalls(){const e=this._spaceModel();return e?Fp(this._curSpaceCfg,e,_f,.02*this._gridPitch):{style:Dp(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){return this._editorRuntimeOrThrow()._deleteRoomClick(e)}get _wallKeyPitch(){return Fa}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_intervalCm(e){const t=this._spaceModel();return t?ms(t.rooms,this._spaceWalls,this._openCuts(),e,this._wallKeyPitch,this._cellCm,this._gridPitch,_f):0}_normalizeWalls(e,t){const i=this._spaceModel();if(!i)return[];return Co(ps(i.rooms,e,t,this._wallKeyPitch,this._cellCm,this._gridPitch,_f),this._curSpaceCfg?.rooms||[],Fa,1,t.map(e=>[e[0]/_f,e[1]/_f,e[2]/_f,e[3]/_f]))}_paperShapes(e){if(!this._spaceWalls.length)return Lt(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:Lt(e)}_wallUnionGeometry(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._physicalBodiesR();if(!t.length&&!i.length)return null;const n=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==n){const r=_g(this._wallUnionPool,n);if(r.hit)this._wallUnionCache=r.value;else{const r=this._openCuts(),o=this._roomWallOpeningInputs(),s=Fs(e.rooms,t,r,o,this._wallKeyPitch,this._cellCm,this._gridPitch,_f,i);s&&Object.defineProperty(s,"sourceFingerprint",{value:Il([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const a={key:n,value:s};gg(this._wallUnionPool,n,a,8),this._wallUnionCache=a}}return this._wallUnionCache.value}_thickWallCuts(){const e=this._spaceModel();if(!e)return[];const t=this._spaceWalls;if(!t.length)return[];const i=this._openCuts();return Is(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,r=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),s=`${e.id}|${this._cfgEpoch}|${t}|${o}`,a=_g(this._innerContourCache,s);if(a.hit)return a.value;const l=fs(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,_f,n,r);return gg(this._innerContourCache,s,l,600),l}_wallThickHit(e){return this._editorRuntimeOrThrow()._wallThickHit(e)}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const e=this._wallThickHit(this._cursorPt);if(!e)return null;const t=function(e,t,i){const n=Number.isFinite(e)&&e>0?e:0,r=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return n>0?po(n,r,o)/2:ya(1.5*o,r)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const n of e.segs)i+=(i?" ":"")+Wo([[n[0],n[1]],[n[2],n[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){return this._editorRuntimeOrThrow()._wallThickClick(e)}_wallThickApply(e){return this._editorRuntimeOrThrow()._wallThickApply(e)}_wallHatchDefs(e){const t=lo(this._cellCm);return j` + `}_zeroWalls(){const e=this._spaceModel();return e?Ip(this._curSpaceCfg,e,yf,.02*this._gridPitch):{style:Ap(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){return this._editorRuntimeOrThrow()._deleteRoomClick(e)}get _wallKeyPitch(){return Oa}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_intervalCm(e){const t=this._spaceModel();return t?ms(t.rooms,this._spaceWalls,this._openCuts(),e,this._wallKeyPitch,this._cellCm,this._gridPitch,yf):0}_normalizeWalls(e,t){const i=this._spaceModel();if(!i)return[];return Co(ps(i.rooms,e,t,this._wallKeyPitch,this._cellCm,this._gridPitch,yf),this._curSpaceCfg?.rooms||[],Oa,1,t.map(e=>[e[0]/yf,e[1]/yf,e[2]/yf,e[3]/yf]))}_paperShapes(e){if(!this._spaceWalls.length)return Lt(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:Lt(e)}_wallUnionGeometry(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._physicalBodiesR();if(!t.length&&!i.length)return null;const n=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==n){const r=yg(this._wallUnionPool,n);if(r.hit)this._wallUnionCache=r.value;else{const r=this._openCuts(),o=this._roomWallOpeningInputs(),s=Fs(e.rooms,t,r,o,this._wallKeyPitch,this._cellCm,this._gridPitch,yf,i);s&&Object.defineProperty(s,"sourceFingerprint",{value:El([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const a={key:n,value:s};bg(this._wallUnionPool,n,a,8),this._wallUnionCache=a}}return this._wallUnionCache.value}_thickWallCuts(){const e=this._spaceModel();if(!e)return[];const t=this._spaceWalls;if(!t.length)return[];const i=this._openCuts();return Is(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,r=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),s=`${e.id}|${this._cfgEpoch}|${t}|${o}`,a=yg(this._innerContourCache,s);if(a.hit)return a.value;const l=fs(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,yf,n,r);return bg(this._innerContourCache,s,l,600),l}_wallThickHit(e){return this._editorRuntimeOrThrow()._wallThickHit(e)}get _wallThickHover(){if(!this._markup||"wallthick"!==this._tool||!this._cursorPt||this._wallDialog)return null;const e=this._wallThickHit(this._cursorPt);if(!e)return null;const t=function(e,t,i){const n=Number.isFinite(e)&&e>0?e:0,r=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return n>0?po(n,r,o)/2:ba(1.5*o,r)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const n of e.segs)i+=(i?" ":"")+Wo([[n[0],n[1]],[n[2],n[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){return this._editorRuntimeOrThrow()._wallThickClick(e)}_wallThickApply(e){return this._editorRuntimeOrThrow()._wallThickApply(e)}_wallHatchDefs(e){const t=lo(this._cellCm);return j` - `}_resolvedRoomFills(e,t){const i=new Map,n=new Map;for(const r of e.rooms){const e=this._roomDialog&&r.id===this._roomEditId?this._roomFill||t.fill:yn(t.fill,r),o=this._roomDialog&&r.id===this._roomEditId?this._roomCustomFill||t.customFill:Ui(t.customFill,r),s=Ji(e,"lqi"===e&&r.area?this._roomLqi(r.area):null,"light"===e?Kh(Vh(this._renderPlanHass,this._renderDevices,r,this._virtualLights)):"none","temp"===e?this._roomTemp(r):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(r,s),r.id&&n.set(r.id,s)}return{byRoom:i,byId:n}}_spaceDisplayForRender(){const e=Vi(this._curSpaceCfg),t=this._spaceDialog;return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,showBorders:t.showBorders,showNames:t.showNames,hideDecor:t.hideDecor,hideOpenings:t.hideOpenings,color:t.roomColor,opacity:t.roomOpacity,fill:t.fillMode,customFill:t.customFill?ji(t.customFill):Wi,glow:t.glowEnabled,tempMin:t.tempMin,tempMax:t.tempMax,showLqi:t.showLqi,cardFontScale:t.cardFontScale,labelTemp:t.labelTemp,labelHum:t.labelHum,labelLqi:t.labelLqi,labelLight:t.labelLight}:e}_openingWallIndexFor(e,t){const i=e.rooms.map(e=>`${e.id}:${e.poly?.map(e=>e.join(",")).join("/")||`${e.x},${e.y},${e.w},${e.h}`}`).join(";"),n=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),r=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,n,r].join("|");let s=this._openingWallIndexCache.get(o);return s||(s=Ns(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,_f)),gg(this._openingWallIndexCache,o,s,4),{key:o,value:s}}_renderOpeningTunnelFills(e,t,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return j``;if("glow-base"===i&&![...t.byRoom.values()].some(Boolean))return j``;const n=this._openCuts(),r=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=r.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),s=this._openingWallIndexFor(e,n),a=`${s.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===a||(this._openingTunnelCache={key:a,value:Us(s.value,r)}),yp({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:t.byId,idPrefix:`${e.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(e,t,i){const n=new Map,r=new Map,o=this._fillColors.glow_base;for(const s of e.rooms){const e=i.byRoom.get(s),a=bn(t.glow,s)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;n.set(s,a),s.id&&r.set(s.id,a)}return{byRoom:n,byId:r}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,Ht(e)])),n=this._openCuts(),r=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",s=e.rooms.map(s=>{const a=t.byRoom.get(s)||null,l=i.get(s)||null;if(!a||!l)return V;const c=this._spaceWalls.length&&s.id&&this._innerRoomContour(e,s.id,n,r)||l,h=ci(c,e.rooms.filter(e=>e!==s).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(s,c,e).path;return d||h.length?j``}_resolvedRoomFills(e,t){const i=new Map,n=new Map;for(const r of e.rooms){const e=this._roomDialog&&r.id===this._roomEditId?this._roomFill||t.fill:yn(t.fill,r),o=this._roomDialog&&r.id===this._roomEditId?this._roomCustomFill||t.customFill:Ui(t.customFill,r),s=Ji(e,"lqi"===e&&r.area?this._roomLqi(r.area):null,"light"===e?Xh(Kh(this._renderPlanHass,this._renderDevices,r,this._virtualLights)):"none","temp"===e?this._roomTemp(r):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(r,s),r.id&&n.set(r.id,s)}return{byRoom:i,byId:n}}_spaceDisplayForRender(){const e=Vi(this._curSpaceCfg),t=this._spaceDialog;return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,showBorders:t.showBorders,showNames:t.showNames,hideDecor:t.hideDecor,hideOpenings:t.hideOpenings,color:t.roomColor,opacity:t.roomOpacity,fill:t.fillMode,customFill:t.customFill?ji(t.customFill):Wi,glow:t.glowEnabled,tempMin:t.tempMin,tempMax:t.tempMax,showLqi:t.showLqi,cardFontScale:t.cardFontScale,labelTemp:t.labelTemp,labelHum:t.labelHum,labelLqi:t.labelLqi,labelLight:t.labelLight}:e}_openingWallIndexFor(e,t){const i=e.rooms.map(e=>`${e.id}:${e.poly?.map(e=>e.join(",")).join("/")||`${e.x},${e.y},${e.w},${e.h}`}`).join(";"),n=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),r=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,n,r].join("|");let s=this._openingWallIndexCache.get(o);return s||(s=Hs(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,yf)),bg(this._openingWallIndexCache,o,s,4),{key:o,value:s}}_renderOpeningTunnelFills(e,t,i="data"){if(this._markup||!this._spaceWalls.length||!this._openingsR.length)return j``;if("glow-base"===i&&![...t.byRoom.values()].some(Boolean))return j``;const n=this._openCuts(),r=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=r.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),s=this._openingWallIndexFor(e,n),a=`${s.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===a||(this._openingTunnelCache={key:a,value:Vs(s.value,r)}),wp({openings:this._openingsR,geometries:this._openingTunnelCache.value,fillsByRoomId:t.byId,idPrefix:`${e.id}-${i}`,groupClass:"data"===i?"opening-tunnels":"opening-tunnels glow-base-tunnels",dataLayer:i})}_resolvedGlowBase(e,t,i){const n=new Map,r=new Map,o=this._fillColors.glow_base;for(const s of e.rooms){const e=i.byRoom.get(s),a=bn(t.glow,s)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;n.set(s,a),s.id&&r.set(s.id,a)}return{byRoom:n,byId:r}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,Ht(e)])),n=this._openCuts(),r=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",s=e.rooms.map(s=>{const a=t.byRoom.get(s)||null,l=i.get(s)||null;if(!a||!l)return V;const c=this._spaceWalls.length&&s.id&&this._innerRoomContour(e,s.id,n,r)||l,h=ci(c,e.rooms.filter(e=>e!==s).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(s,c,e).path;return d||h.length?j``:s.poly||c!==l?j``)} - `}_isoDecorationLayers(e){const t="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(e){const t=!!e.showBorders;return{structural:t,panels:t&&!e.hideOpenings,shadows:t&&e.filtersSupported&&!e.forcedColors,materialNuance:t&&!e.forcedColors,floorSymbols:!e.hideOpenings&&!t}}({showBorders:e.showBorders,hideOpenings:e.hideOpenings,filtersSupported:t,forcedColors:i})}_isoOpeningPanels(e){if(!e.panels)return[];const t=this._isoScene();if(!t)return[];return t.openings.flatMap(e=>{const t=this._openingsR[e.sourceIndex];return t?Wg(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=va(this._cellCm);return j` + `}_isoDecorationLayers(e){const t="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("filter","blur(1px)"),i="function"==typeof matchMedia&&matchMedia("(forced-colors: active)").matches;return function(e){const t=!!e.showBorders;return{structural:t,panels:t&&!e.hideOpenings,shadows:t&&e.filtersSupported&&!e.forcedColors,materialNuance:t&&!e.forcedColors,floorSymbols:!e.hideOpenings&&!t}}({showBorders:e.showBorders,hideOpenings:e.hideOpenings,filtersSupported:t,forcedColors:i})}_isoOpeningPanels(e){if(!e.panels)return[];const t=this._isoScene();if(!t)return[];return t.openings.flatMap(e=>{const t=this._openingsR[e.sourceIndex];return t?Gg(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=ya(this._cellCm);return j` ${"walls"===t&&e.materialNuance?j` @@ -5079,7 +5079,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef `}_renderIsoUnderlay(e){if(!e.structural)return j``;const t=this._isoScene()?.floor;return t?j``:j``}_renderIsoShadows(e,t){if(!e.shadows)return j``;const i=this._isoScene()?.geometry;return i?j``:j``}_roomHoverPaths(e){const t=this._hoverRoom;if("view"!==this._mode||!t||t.space!==e.id)return null;const i=e.rooms.find(e=>e===t.room||!!e.id&&e.id===t.room.id);if(!i)return null;const n=Ht(i);if(!n)return null;const r=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:Ht(e)})).filter(e=>!!e.poly),o=ci(n,r.map(e=>e.poly)),s=this._openCuts(),a=.02*this._gridPitch,l=s.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return n.some((e,i)=>Mn(t,[e[0],e[1],...n[(i+1)%n.length]])<=4*a)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,s,h)||n,u=[{axis:n,face:d}];for(const t of o){const i=r.find(e=>e.poly===t)?.room;let n=t;if(c.length&&i?.id){const r=hs(e.rooms,i.id,c,s,this._wallKeyPitch,this._cellCm,this._gridPitch,_f);r&&(n=Es(r.poly,r.offsets)||t)}u.push({axis:t,face:n})}const p=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,n=Math.sin(t)*e.length/2;return[e.x-i,e.y-n,e.x+i,e.y+n]}),m=l.concat(p),_=u.map(({axis:e,face:t})=>{const i=m.map(i=>((e,t,i)=>{const n=e[2]-e[0],r=e[3]-e[1],o=Math.hypot(n,r);if(o.05)&&Mn([c,h],[i[0],i[1],n[0],n[1]])<=4*a){d=!0;break}}if(!d)return null;let u=null;for(let e=0;e.05)continue;const p=Mn([c,h],[t[0],t[1],n[0],n[1]]);(!u||p!!e);return i.length?mn(t,i,a).map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" "):`M ${t.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!_)return null;const g=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,e).path||g(d),...o.map(g)].join(" "),outlineD:_}}_renderRoomHoverFill(e){return e?j` - - `:j``}_renderRoomHoverOutline(e){return e?j` - - - `:j``}_renderWallThickUi(){return this._editorRuntime?this._editorRuntimeOrThrow()._renderWallThickUi():j``}_renderWallThickDialog(){return this._editorRuntimeOrThrow()._renderWallThickDialog()}_openingAt(e){return this._editorRuntimeOrThrow()._openingAt(e)}_resolveOpeningPlacement(e){return this._editorRuntimeOrThrow()._resolveOpeningPlacement(e)}_activateOpeningPlacement(e){return this._editorRuntimeOrThrow()._activateOpeningPlacement(e)}_clearOpeningPlacement(e){return this._editorRuntime?this._editorRuntimeOrThrow()._clearOpeningPlacement(e):(this._openingHoverCandidate=null,this._openingJambBlockCm=null,void(e&&(this._openingPreset=null,this._openingRebindId=null)))}_openingClick(e){return this._editorRuntimeOrThrow()._openingClick(e)}_editOpening(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._editOpening(e)}_opPointerDown(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerDown(e,t)}_opPointerMove(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerMove(e,t)}_opRuler(e,t){return this._editorRuntimeOrThrow()._opRuler(e,t)}_opPointerUp(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerUp(e,t)}_opClick(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opClick(e,t)}_saveOpening(){return this._editorRuntimeOrThrow()._saveOpening()}_deleteOpening(){return this._editorRuntimeOrThrow()._deleteOpening()}_contactCandidates(){return this._editorRuntimeOrThrow()._contactCandidates()}_lockCandidates(){return this._editorRuntimeOrThrow()._lockCandidates()}_toggleOpeningEntityPicker(e){return this._editorRuntimeOrThrow()._toggleOpeningEntityPicker(e)}_filterOpeningEntities(e,t){return this._editorRuntimeOrThrow()._filterOpeningEntities(e,t)}_selectOpeningEntity(e,t){return this._editorRuntimeOrThrow()._selectOpeningEntity(e,t)}_mergeClick(e){return this._editorRuntimeOrThrow()._mergeClick(e)}_commitMerge(){return this._editorRuntimeOrThrow()._commitMerge()}_splitClick(e){return this._editorRuntimeOrThrow()._splitClick(e)}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])&&(null!=this._closingWallCm||!this._activeDraftId)}_markupMove(e){if(this._markup&&this._editorRuntime)return this._editorRuntime._markupMove(e)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&Jg(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const n=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=n,n}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){return this._editorRuntimeOrThrow()._saveRoom()}_decideWallFace(e){return this._editorRuntimeOrThrow()._decideWallFace(e)}_wallSourceCmAt(e,t,i){return this._editorRuntimeOrThrow()._wallSourceCmAt(e,t,i)}_activePathWithRepair(e,t){return this._editorRuntimeOrThrow()._activePathWithRepair(e,t)}_validateWallRepair(e,t){return this._editorRuntimeOrThrow()._validateWallRepair(e,t)}_applyWallRepair(e,t){return this._editorRuntimeOrThrow()._applyWallRepair(e,t)}_applyWallFaceBatch(){return this._editorRuntimeOrThrow()._applyWallFaceBatch()}_commitRoom(){return this._editorRuntimeOrThrow()._commitRoom()}_cancelPath(){return this._editorRuntimeOrThrow()._cancelPath()}_resumeLastDraft(){return this._editorRuntimeOrThrow()._resumeLastDraft()}_roomDialogCancel(){return this._editorRuntimeOrThrow()._roomDialogCancel()}get _freeAreas(){const e=new Set;for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area&&e.add(i.area);for(const t of this._wallFaceBatch?.decisions||[])t.create&&t.area&&e.add(t.area);return Object.values(this.hass?.areas||{}).filter(t=>!e.has(t.area_id)).sort((e,t)=>(e.name||"").localeCompare(t.name||""))}_openDeviceInbox(){return this._editorRuntimeOrThrow()._openDeviceInbox()}_closeMarkerDialog(){return this._editorRuntimeOrThrow()._closeMarkerDialog()}_deviceInboxCandidates(e){return this._editorRuntimeOrThrow()._deviceInboxCandidates(e)}_deviceInboxRows(){return this._editorRuntimeOrThrow()._deviceInboxRows()}_deviceForInboxRow(e){return this._editorRuntimeOrThrow()._deviceForInboxRow(e)}_openInboxMarker(e,t=!1){return this._editorRuntimeOrThrow()._openInboxMarker(e,t)}async _setInboxHidden(e,t){return this._editorRuntimeOrThrow()._setInboxHidden(e,t)}_findInboxDevice(e){return this._editorRuntimeOrThrow()._findInboxDevice(e)}_deviceInboxTabKey(e){return this._editorRuntimeOrThrow()._deviceInboxTabKey(e)}_openMarkerDialog(e){if(this._editorRuntime)return this._editorRuntime._openMarkerDialog(e);this._ensureEditorRuntime().then(t=>{t&&this._openMarkerDialog(e)})}_runCandidates(){return this._editorRuntimeOrThrow()._runCandidates()}_bindingCandidates(){return this._editorRuntimeOrThrow()._bindingCandidates()}_physicalBodiesR(e=this._spaceModel()){if(!e)return[];const t=`${e.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===t)return this._physicalBodiesCache.all;const i=oa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e));return this._physicalBodiesCache={key:t,...i},i.all}_rawPhysicalBodiesR(e=this._spaceModel()){if(!e)return[];this._physicalBodiesR(e);const t=this._physicalBodiesCache;return t?[...t.drafts,...t.partitions,...t.columns]:[]}_cleanFloor(e,t,i=this._spaceModel()){if(!i)return{floor:t,geom:null,path:"",area:da([[[...t,t[0]]]])};const n=e.id||`#${i.rooms.indexOf(e)}`,r=`${i.id}|${this._cfgEpoch}|${n}`;if(!this._resize?.preview){const e=_g(this._cleanFloorCache,r);if(e.hit)return e.value}const o=t.map(e=>e[0]),s=t.map(e=>e[1]),a=[Math.min(...o),Math.min(...s),Math.max(...o),Math.max(...s)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=a[0]&&Math.min(...t)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?ha(t,l):null,h={floor:t,geom:c,path:c?Qs(c):"",area:da(c||[[[...t,t[0]]]])};return this._resize?.preview||gg(this._cleanFloorCache,r,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const n=this._fullRegistryHass,r=this._bindingStatus(e),o="active"===r.kind?r.enabledEntityIds:r.allEntityIds;if("device"===t){const e=n.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":td(n,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=n.entities?.[i],t=this.hass.states?.[i],r=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":td(n,r,"",[i],this._iconRules)}return""}_allRoomsFlat(){const e=[];for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area?e.push({value:t.id+"#"+i.area,label:(t.title||t.id)+" · "+i.name}):i.id&&e.push({value:t.id+"#@"+i.id,label:(t.title||t.id)+" · "+i.name+" · "+this._t("marker.subarea")});return e}_errText(e){if(!e)return this._t("err.unknown");if("string"==typeof e)return e;if("invalid_passage_fields"===e.code){const t=String(e.message||e.error||"");let i="",n=[];try{const e=JSON.parse(t);i=String(e.space??""),n=Array.isArray(e.fields)?e.fields.map(String):[]}catch{const e=t.match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);e&&(i=e[1],n=e[3].split(",").filter(Boolean))}if(i||n.length){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===i),t={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},r=n.map(e=>t[e]?this._t(t[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||i,fields:r})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"");let i=NaN;try{i=Number(JSON.parse(t).margin_cm)}catch{const e=t.match(/margin_cm=([^;}"]*)/);i=e?Number(e[1]):NaN}if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:Et(i,this._imperial)})}if(null!=e.code){const t=`backup.error.${e.code}`,i=this._t(t);return e.message&&console.warn("[houseplan] backend error",e.code,e.message),i!==t?i:this._t("err.code",{code:e.code})}if(e.message)return e.message;if(e.error)return e.error;try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){return this._editorRuntimeOrThrow()._backupErrorText(e)}async _pickMarkerFiles(e){return this._editorRuntimeOrThrow()._pickMarkerFiles(e)}_removeMarkerPdf(e){return this._editorRuntimeOrThrow()._removeMarkerPdf(e)}_markerLightFields(e){return this._editorRuntimeOrThrow()._markerLightFields(e)}_markerTapActionFields(e){return this._editorRuntimeOrThrow()._markerTapActionFields(e)}_markerToggleEntityFields(e){return this._editorRuntimeOrThrow()._markerToggleEntityFields(e)}async _saveMarker(){return this._editorRuntimeOrThrow()._saveMarker()}async _deleteMarker(){return this._editorRuntimeOrThrow()._deleteMarker()}_normPos(e,t,i){return{s:e,x:t/_f,y:i/_f}}_spaceDialogUsesOnboardingRuntime(e){return"create"===e&&!this._editorRuntime&&(!!this._onboardingRuntime||0===(this._serverCfg?.spaces.length||0)||this._importTotal>0||this._importQueue.length>0)}_spaceRuntimeOrThrow(){if(!this._onboardingRuntime)throw new Error("Houseplan onboarding runtime is not loaded");return this._onboardingRuntime}_openSpaceDialog(e,t){return this._spaceDialogUsesOnboardingRuntime(e)?this._onboardingRuntime?void this._onboardingRuntime._openSpaceDialog(e,t):void this._ensureOnboardingRuntime().then(i=>{i&&this._spaceRuntimeOrThrow()._openSpaceDialog(e,t)}):this._editorRuntime?this._editorRuntime._openSpaceDialog(e,t):void this._ensureEditorRuntime().then(i=>{i&&this._openSpaceDialog(e,t)})}async _pickPlanFile(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._pickPlanFile(e):this._editorRuntimeOrThrow()._pickPlanFile(e)}_useServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._useServerPlan(e):this._editorRuntimeOrThrow()._useServerPlan(e)}async _readPlanAspect(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._readPlanAspect(e):this._editorRuntimeOrThrow()._readPlanAspect(e)}async _deleteServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._deleteServerPlan(e):this._editorRuntimeOrThrow()._deleteServerPlan(e)}_renderServerPlans(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime(e.mode)?this._onboardingRuntime._renderServerPlans(e):this._editorRuntimeOrThrow()._renderServerPlans(e)}async _saveSpaceDialog(){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._saveSpaceDialog():this._editorRuntimeOrThrow()._saveSpaceDialog()}async _deleteSpace(){return this._editorRuntimeOrThrow()._deleteSpace()}async _saveConfigNow(){return this._editorRuntimeOrThrow()._saveConfigNow()}_startImport(){return this._onboardingRuntime?this._onboardingRuntime._startImport():this._editorRuntimeOrThrow()._startImport()}_openNextImport(){return this._onboardingRuntime?this._onboardingRuntime._openNextImport():this._editorRuntimeOrThrow()._openNextImport()}_skipImport(){return this._onboardingRuntime?this._onboardingRuntime._skipImport():this._editorRuntimeOrThrow()._skipImport()}_renderImportDialog(){return this._onboardingRuntime?this._onboardingRuntime._renderImportDialog():this._editorRuntimeOrThrow()._renderImportDialog()}_sunGlobal(){const e=this._settingsDialog;return e?{...this._settings,north_deg:e.northDeg??void 0,bg_mode:e.bgMode,sun_rays:e.sunRays}:this._settings}_sunSpace(){const e=this._spaceDialog,t=this._curSpaceCfg?.settings||{};return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,north_deg:e.northDeg??void 0,bg_mode:e.bgMode??void 0,sun_rays:e.sunRays??void 0}:t}_effNorth(){return Zn(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Jn(this._sunGlobal(),this._sunSpace())}_dayCycleState(e=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:Bn(this._renderPlanHass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?qn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Qn(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?er(this._renderPlanHass):null}_renderSunRays(e){const t=j``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),t;const n=this._effNorth(),r=null!==n?er(this._renderPlanHass):null;if(!r||r.elevation<=0)return this._sunFadeReset(),t;if(o=r.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return t;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const s=this._zeroWalls(),a=s.barriers.map(e=>e.join(",")).join(";"),l=`${e.id}|${r.azimuth}|${r.elevation}|${n}|${this._cfgEpoch}|${s.style}|${a}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const t=e.rooms.map(e=>({id:e.id||"",poly:Ht(e)})).filter(e=>!!e.id&&!!e.poly),i=this._openingsR.filter(e=>"window"===e.type&&"partition"!==e.host?.kind).map(e=>({id:e.id,x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=this._spaceWalls,a=this._openCuts(),c=this._openingWallIndexFor(e,a).value,h={},d={},u=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of t){const t=this._innerRoomContour(e,i.id,a,u);t&&(h[i.id]=t)}for(const e of i){const t=qs(c,{x:e.x,y:e.y,angle:e.angle,length:e.length});t.cm>0&&(d[e.id]=po(t.cm,this._cellCm,this._gridPitch))}}let p=Yn(t,i,r.azimuth,r.elevation,n,o.length?h:void 0,o.length?d:void 0);const m=[...this._physicalBodiesR(e),...s.barriers.map(e=>[[e[0],e[1]],[e[2],e[3]]])];m.length&&(p=p.map(e=>{const t=function(e,t,i){return i>0?e.map(e=>function(e){const t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(t.length<=2)return t;const i=(e,t,i)=>(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0]),n=[];for(const e of t){for(;n.length>=2&&i(n[n.length-2],n[n.length-1],e)<=0;)n.pop();n.push(e)}const r=[];for(let e=t.length-1;e>=0;e--){const n=t[e];for(;r.length>=2&&i(r[r.length-2],r[r.length-1],n)<=0;)r.pop();r.push(n)}return n.slice(0,-1).concat(r.slice(0,-1))}([...e,...e.map(e=>[e[0]+t[0]*i,e[1]+t[1]*i])])).filter(e=>e.length>=3):e}(m,e.dir,e.len),i=e.polys.map(e=>ha(e,t));return{...e,paths:i.map(Qs).filter(Boolean),polys:i.flatMap(pa)}}).filter(e=>e.paths?.length||e.polys.length)),this._sunRaysCache={key:l,rays:p,rims:p.map(e=>function(e,t=1e-4){const[i,n]=e.dir,r=-n,o=i,s=[];for(const a of[e.a,e.b]){const l=[];for(const s of e.polys)for(let e=0;et)continue;if(Math.abs((h[0]-a[0])*r+(h[1]-a[1])*o)>t)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*n,u=(h[0]-a[0])*i+(h[1]-a[1])*n;Math.abs(u-d)<=t||l.push(de[0]-t[0]);const c=[];for(const e of l){const i=c[c.length-1];i&&e[0]<=i[1]+t?i[1]=Math.max(i[1],e[1]):c.push([e[0],e[1]])}for(const[e,t]of c)s.push([[a[0]+i*e,a[1]+n*e],[a[0]+i*t,a[1]+n*t]])}return s}(e))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return t;const d=(u=function(e){const t=Math.min(90,Math.max(-90,Number(e)||0));let i=Wn[Wn.length-1][1];for(let e=1;e + `:j``}_roomHoverPaths(e){const t=this._hoverRoom;if("view"!==this._mode||!t||t.space!==e.id)return null;const i=e.rooms.find(e=>e===t.room||!!e.id&&e.id===t.room.id);if(!i)return null;const n=Ht(i);if(!n)return null;const r=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:Ht(e)})).filter(e=>!!e.poly),o=ci(n,r.map(e=>e.poly)),s=this._openCuts(),a=.02*this._gridPitch,l=s.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return n.some((e,i)=>Mn(t,[e[0],e[1],...n[(i+1)%n.length]])<=4*a)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,s,h)||n,u=[{axis:n,face:d}];for(const t of o){const i=r.find(e=>e.poly===t)?.room;let n=t;if(c.length&&i?.id){const r=hs(e.rooms,i.id,c,s,this._wallKeyPitch,this._cellCm,this._gridPitch,yf);r&&(n=Ns(r.poly,r.offsets)||t)}u.push({axis:t,face:n})}const p=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,n=Math.sin(t)*e.length/2;return[e.x-i,e.y-n,e.x+i,e.y+n]}),m=l.concat(p),_=u.map(({axis:e,face:t})=>{const i=m.map(i=>((e,t,i)=>{const n=e[2]-e[0],r=e[3]-e[1],o=Math.hypot(n,r);if(o.05)&&Mn([c,h],[i[0],i[1],n[0],n[1]])<=4*a){d=!0;break}}if(!d)return null;let u=null;for(let e=0;e.05)continue;const p=Mn([c,h],[t[0],t[1],n[0],n[1]]);(!u||p!!e);return i.length?mn(t,i,a).map(e=>`M ${e[0]} ${e[1]} L ${e[2]} ${e[3]}`).join(" "):`M ${t.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`}).filter(Boolean).join(" ");if(!_)return null;const g=e=>`M ${e.map(e=>`${e[0]} ${e[1]}`).join(" L ")} Z`;return{fillD:[this._cleanFloor(i,d,e).path||g(d),...o.map(g)].join(" "),outlineD:_}}_renderRoomHoverFill(e){return j` + + `}_renderRoomHoverOutline(e){return j` + + + `}_renderWallThickUi(){return this._editorRuntime?this._editorRuntimeOrThrow()._renderWallThickUi():j``}_renderWallThickDialog(){return this._editorRuntimeOrThrow()._renderWallThickDialog()}_openingAt(e){return this._editorRuntimeOrThrow()._openingAt(e)}_resolveOpeningPlacement(e){return this._editorRuntimeOrThrow()._resolveOpeningPlacement(e)}_activateOpeningPlacement(e){return this._editorRuntimeOrThrow()._activateOpeningPlacement(e)}_clearOpeningPlacement(e){return this._editorRuntime?this._editorRuntimeOrThrow()._clearOpeningPlacement(e):(this._openingHoverCandidate=null,this._openingJambBlockCm=null,void(e&&(this._openingPreset=null,this._openingRebindId=null)))}_openingClick(e){return this._editorRuntimeOrThrow()._openingClick(e)}_editOpening(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._editOpening(e)}_opPointerDown(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerDown(e,t)}_opPointerMove(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerMove(e,t)}_opRuler(e,t){return this._editorRuntimeOrThrow()._opRuler(e,t)}_opPointerUp(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opPointerUp(e,t)}_opClick(e,t){if(this._editorRuntime)return this._editorRuntimeOrThrow()._opClick(e,t)}_saveOpening(){return this._editorRuntimeOrThrow()._saveOpening()}_deleteOpening(){return this._editorRuntimeOrThrow()._deleteOpening()}_contactCandidates(){return this._editorRuntimeOrThrow()._contactCandidates()}_lockCandidates(){return this._editorRuntimeOrThrow()._lockCandidates()}_toggleOpeningEntityPicker(e){return this._editorRuntimeOrThrow()._toggleOpeningEntityPicker(e)}_filterOpeningEntities(e,t){return this._editorRuntimeOrThrow()._filterOpeningEntities(e,t)}_selectOpeningEntity(e,t){return this._editorRuntimeOrThrow()._selectOpeningEntity(e,t)}_mergeClick(e){return this._editorRuntimeOrThrow()._mergeClick(e)}_commitMerge(){return this._editorRuntimeOrThrow()._commitMerge()}_splitClick(e){return this._editorRuntimeOrThrow()._splitClick(e)}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])&&(null!=this._closingWallCm||!this._activeDraftId)}_markupMove(e){this._markup&&this._editorRuntime&&this._editorRuntimeOrThrow()._queuePointerMove("markup-hover",()=>this._editorRuntime._markupMove(e))}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&nf(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const n=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=n,n}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){return this._editorRuntimeOrThrow()._saveRoom()}_decideWallFace(e){return this._editorRuntimeOrThrow()._decideWallFace(e)}_wallSourceCmAt(e,t,i){return this._editorRuntimeOrThrow()._wallSourceCmAt(e,t,i)}_activePathWithRepair(e,t){return this._editorRuntimeOrThrow()._activePathWithRepair(e,t)}_validateWallRepair(e,t){return this._editorRuntimeOrThrow()._validateWallRepair(e,t)}_applyWallRepair(e,t){return this._editorRuntimeOrThrow()._applyWallRepair(e,t)}_applyWallFaceBatch(){return this._editorRuntimeOrThrow()._applyWallFaceBatch()}_commitRoom(){return this._editorRuntimeOrThrow()._commitRoom()}_cancelPath(){return this._editorRuntimeOrThrow()._cancelPath()}_resumeLastDraft(){return this._editorRuntimeOrThrow()._resumeLastDraft()}_roomDialogCancel(){return this._editorRuntimeOrThrow()._roomDialogCancel()}get _freeAreas(){const e=new Set;for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area&&e.add(i.area);for(const t of this._wallFaceBatch?.decisions||[])t.create&&t.area&&e.add(t.area);return Object.values(this.hass?.areas||{}).filter(t=>!e.has(t.area_id)).sort((e,t)=>(e.name||"").localeCompare(t.name||""))}_openDeviceInbox(){return this._editorRuntimeOrThrow()._openDeviceInbox()}_closeMarkerDialog(){return this._editorRuntimeOrThrow()._closeMarkerDialog()}_deviceInboxCandidates(e){return this._editorRuntimeOrThrow()._deviceInboxCandidates(e)}_deviceInboxRows(){return this._editorRuntimeOrThrow()._deviceInboxRows()}_deviceForInboxRow(e){return this._editorRuntimeOrThrow()._deviceForInboxRow(e)}_openInboxMarker(e,t=!1){return this._editorRuntimeOrThrow()._openInboxMarker(e,t)}async _setInboxHidden(e,t){return this._editorRuntimeOrThrow()._setInboxHidden(e,t)}_findInboxDevice(e){return this._editorRuntimeOrThrow()._findInboxDevice(e)}_deviceInboxTabKey(e){return this._editorRuntimeOrThrow()._deviceInboxTabKey(e)}_openMarkerDialog(e){if(this._editorRuntime)return this._editorRuntime._openMarkerDialog(e);this._ensureEditorRuntime().then(t=>{t&&this._openMarkerDialog(e)})}_runCandidates(){return this._editorRuntimeOrThrow()._runCandidates()}_bindingCandidates(){return this._editorRuntimeOrThrow()._bindingCandidates()}_physicalBodiesR(e=this._spaceModel()){if(!e)return[];const t=`${e.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===t)return this._physicalBodiesCache.all;const i=sa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e));return this._physicalBodiesCache={key:t,...i},i.all}_rawPhysicalBodiesR(e=this._spaceModel()){if(!e)return[];this._physicalBodiesR(e);const t=this._physicalBodiesCache;return t?[...t.drafts,...t.partitions,...t.columns]:[]}_cleanFloor(e,t,i=this._spaceModel()){if(!i)return{floor:t,geom:null,path:"",area:ua([[[...t,t[0]]]])};const n=e.id||`#${i.rooms.indexOf(e)}`,r=`${i.id}|${this._cfgEpoch}|${n}`;if(!this._resize?.preview){const e=yg(this._cleanFloorCache,r);if(e.hit)return e.value}const o=t.map(e=>e[0]),s=t.map(e=>e[1]),a=[Math.min(...o),Math.min(...s),Math.max(...o),Math.max(...s)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=a[0]&&Math.min(...t)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?da(t,l):null,h={floor:t,geom:c,path:c?ea(c):"",area:ua(c||[[[...t,t[0]]]])};return this._resize?.preview||bg(this._cleanFloorCache,r,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const n=this._fullRegistryHass,r=this._bindingStatus(e),o="active"===r.kind?r.enabledEntityIds:r.allEntityIds;if("device"===t){const e=n.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":nd(n,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=n.entities?.[i],t=this.hass.states?.[i],r=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":nd(n,r,"",[i],this._iconRules)}return""}_allRoomsFlat(){const e=[];for(const t of this._serverCfg?.spaces||[])for(const i of t.rooms||[])i.area?e.push({value:t.id+"#"+i.area,label:(t.title||t.id)+" · "+i.name}):i.id&&e.push({value:t.id+"#@"+i.id,label:(t.title||t.id)+" · "+i.name+" · "+this._t("marker.subarea")});return e}_errText(e){if(!e)return this._t("err.unknown");if("string"==typeof e)return e;if("invalid_passage_fields"===e.code){const t=String(e.message||e.error||"");let i="",n=[];try{const e=JSON.parse(t);i=String(e.space??""),n=Array.isArray(e.fields)?e.fields.map(String):[]}catch{const e=t.match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);e&&(i=e[1],n=e[3].split(",").filter(Boolean))}if(i||n.length){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===i),t={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},r=n.map(e=>t[e]?this._t(t[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||i,fields:r})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"");let i=NaN;try{i=Number(JSON.parse(t).margin_cm)}catch{const e=t.match(/margin_cm=([^;}"]*)/);i=e?Number(e[1]):NaN}if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:Et(i,this._imperial)})}if(null!=e.code){const t=`backup.error.${e.code}`,i=this._t(t);return e.message&&console.warn("[houseplan] backend error",e.code,e.message),i!==t?i:this._t("err.code",{code:e.code})}if(e.message)return e.message;if(e.error)return e.error;try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){return this._editorRuntimeOrThrow()._backupErrorText(e)}async _pickMarkerFiles(e){return this._editorRuntimeOrThrow()._pickMarkerFiles(e)}_removeMarkerPdf(e){return this._editorRuntimeOrThrow()._removeMarkerPdf(e)}_markerLightFields(e){return this._editorRuntimeOrThrow()._markerLightFields(e)}_markerTapActionFields(e){return this._editorRuntimeOrThrow()._markerTapActionFields(e)}_markerToggleEntityFields(e){return this._editorRuntimeOrThrow()._markerToggleEntityFields(e)}async _saveMarker(){return this._editorRuntimeOrThrow()._saveMarker()}async _deleteMarker(){return this._editorRuntimeOrThrow()._deleteMarker()}_normPos(e,t,i){return{s:e,x:t/yf,y:i/yf}}_spaceDialogUsesOnboardingRuntime(e){return"create"===e&&!this._editorRuntime&&(!!this._onboardingRuntime||0===(this._serverCfg?.spaces.length||0)||this._importTotal>0||this._importQueue.length>0)}_spaceRuntimeOrThrow(){if(!this._onboardingRuntime)throw new Error("Houseplan onboarding runtime is not loaded");return this._onboardingRuntime}_openSpaceDialog(e,t){return this._spaceDialogUsesOnboardingRuntime(e)?this._onboardingRuntime?void this._onboardingRuntime._openSpaceDialog(e,t):void this._ensureOnboardingRuntime().then(i=>{i&&this._spaceRuntimeOrThrow()._openSpaceDialog(e,t)}):this._editorRuntime?this._editorRuntime._openSpaceDialog(e,t):void this._ensureEditorRuntime().then(i=>{i&&this._openSpaceDialog(e,t)})}async _pickPlanFile(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._pickPlanFile(e):this._editorRuntimeOrThrow()._pickPlanFile(e)}_useServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._useServerPlan(e):this._editorRuntimeOrThrow()._useServerPlan(e)}async _readPlanAspect(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._readPlanAspect(e):this._editorRuntimeOrThrow()._readPlanAspect(e)}async _deleteServerPlan(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._deleteServerPlan(e):this._editorRuntimeOrThrow()._deleteServerPlan(e)}_renderServerPlans(e){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime(e.mode)?this._onboardingRuntime._renderServerPlans(e):this._editorRuntimeOrThrow()._renderServerPlans(e)}async _saveSpaceDialog(){return this._onboardingRuntime&&this._spaceDialogUsesOnboardingRuntime("create")?this._onboardingRuntime._saveSpaceDialog():this._editorRuntimeOrThrow()._saveSpaceDialog()}async _deleteSpace(){return this._editorRuntimeOrThrow()._deleteSpace()}async _saveConfigNow(){return this._editorRuntimeOrThrow()._saveConfigNow()}_startImport(){return this._onboardingRuntime?this._onboardingRuntime._startImport():this._editorRuntimeOrThrow()._startImport()}_openNextImport(){return this._onboardingRuntime?this._onboardingRuntime._openNextImport():this._editorRuntimeOrThrow()._openNextImport()}_skipImport(){return this._onboardingRuntime?this._onboardingRuntime._skipImport():this._editorRuntimeOrThrow()._skipImport()}_renderImportDialog(){return this._onboardingRuntime?this._onboardingRuntime._renderImportDialog():this._editorRuntimeOrThrow()._renderImportDialog()}_sunGlobal(){const e=this._settingsDialog;return e?{...this._settings,north_deg:e.northDeg??void 0,bg_mode:e.bgMode,sun_rays:e.sunRays}:this._settings}_sunSpace(){const e=this._spaceDialog,t=this._curSpaceCfg?.settings||{};return e&&"edit"===e.mode&&e.spaceId===this._space?{...t,north_deg:e.northDeg??void 0,bg_mode:e.bgMode??void 0,sun_rays:e.sunRays??void 0}:t}_effNorth(){return Zn(this._sunGlobal(),this._sunSpace())}_effBgMode(){return Jn(this._sunGlobal(),this._sunSpace())}_dayCycleState(e=new Date){return(this._modeTransitionVisual?.viewWeight??("view"===this._mode?1:0))<=0||"daynight"!==this._effBgMode()?null:Bn(this._renderPlanHass,e)}_syncDayCycleClock(){const e=this._dayCycleState();this._dayCycleClockKey=e?qn(e):"";const t="clock"===e?.source&&"hidden"!==this.ownerDocument.visibilityState&&this.isConnected;t&&!this._dayCycleTimer?this._dayCycleTimer=window.setInterval(this._dayCycleTick,3e4):!t&&this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_dayCycleVisibility(e){"hidden"!==e.kind?(this._dayCycleTick(),this._syncDayCycleClock()):this._dayCycleTimer&&(clearInterval(this._dayCycleTimer),this._dayCycleTimer=0)}_effSunRays(){return Qn(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?er(this._renderPlanHass):null}_renderSunRays(e){const t=j``,i=this._modeTransitionVisual?.viewWeight??0;if(this._editing&&i<=0||!this._effSunRays())return this._sunFadeReset(),t;const n=this._effNorth(),r=null!==n?er(this._renderPlanHass):null;if(!r||r.elevation<=0)return this._sunFadeReset(),t;if(o=r.elevation,Number(o)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return t;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var o;const s=this._zeroWalls(),a=s.barriers.map(e=>e.join(",")).join(";"),l=`${e.id}|${r.azimuth}|${r.elevation}|${n}|${this._cfgEpoch}|${s.style}|${a}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const t=e.rooms.map(e=>({id:e.id||"",poly:Ht(e)})).filter(e=>!!e.id&&!!e.poly),i=this._openingsR.filter(e=>"window"===e.type&&"partition"!==e.host?.kind).map(e=>({id:e.id,x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=this._spaceWalls,a=this._openCuts(),c=this._openingWallIndexFor(e,a).value,h={},d={},u=this._wallUnionGeometry()?.roomGeom;if(o.length){for(const i of t){const t=this._innerRoomContour(e,i.id,a,u);t&&(h[i.id]=t)}for(const e of i){const t=Ws(c,{x:e.x,y:e.y,angle:e.angle,length:e.length});t.cm>0&&(d[e.id]=po(t.cm,this._cellCm,this._gridPitch))}}let p=Yn(t,i,r.azimuth,r.elevation,n,o.length?h:void 0,o.length?d:void 0);const m=[...this._physicalBodiesR(e),...s.barriers.map(e=>[[e[0],e[1]],[e[2],e[3]]])];m.length&&(p=p.map(e=>{const t=function(e,t,i){return i>0?e.map(e=>function(e){const t=[...e].sort((e,t)=>e[0]-t[0]||e[1]-t[1]);if(t.length<=2)return t;const i=(e,t,i)=>(t[0]-e[0])*(i[1]-e[1])-(t[1]-e[1])*(i[0]-e[0]),n=[];for(const e of t){for(;n.length>=2&&i(n[n.length-2],n[n.length-1],e)<=0;)n.pop();n.push(e)}const r=[];for(let e=t.length-1;e>=0;e--){const n=t[e];for(;r.length>=2&&i(r[r.length-2],r[r.length-1],n)<=0;)r.pop();r.push(n)}return n.slice(0,-1).concat(r.slice(0,-1))}([...e,...e.map(e=>[e[0]+t[0]*i,e[1]+t[1]*i])])).filter(e=>e.length>=3):e}(m,e.dir,e.len),i=e.polys.map(e=>da(e,t));return{...e,paths:i.map(ea).filter(Boolean),polys:i.flatMap(ma)}}).filter(e=>e.paths?.length||e.polys.length)),this._sunRaysCache={key:l,rays:p,rims:p.map(e=>function(e,t=1e-4){const[i,n]=e.dir,r=-n,o=i,s=[];for(const a of[e.a,e.b]){const l=[];for(const s of e.polys)for(let e=0;et)continue;if(Math.abs((h[0]-a[0])*r+(h[1]-a[1])*o)>t)continue;const d=(c[0]-a[0])*i+(c[1]-a[1])*n,u=(h[0]-a[0])*i+(h[1]-a[1])*n;Math.abs(u-d)<=t||l.push(de[0]-t[0]);const c=[];for(const e of l){const i=c[c.length-1];i&&e[0]<=i[1]+t?i[1]=Math.max(i[1],e[1]):c.push([e[0],e[1]])}for(const[e,t]of c)s.push([[a[0]+i*e,a[1]+n*e],[a[0]+i*t,a[1]+n*t]])}return s}(e))}}const c=this._sunRaysCache.rays,h=this._sunRaysCache.rims;if(!c.length)return t;const d=(u=function(e){const t=Math.min(90,Math.max(-90,Number(e)||0));let i=Wn[Wn.length-1][1];for(let e=1;e ${c.map((e,t)=>{const i=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2,r=i+e.normal[0]*e.depth,o=n+e.normal[1]*e.depth;return j` ${p.map(([e,t])=>j`${this._t("gs.north_letter")} ${null===e?"—":e+"°"} - `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,n=t?t.bgColor||"":Gi(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||n}_stageBgHex(){const e=this._stageEl;if(e){const t=getComputedStyle(e).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(t)return"#"+t.slice(1,4).map(e=>(+e).toString(16).padStart(2,"0")).join("")}return"#111111"}_preflightDiagnostics(e,t){return this._editorRuntimeOrThrow()._preflightDiagnostics(e,t)}_reportPreflightFailure(e,t){return this._editorRuntimeOrThrow()._reportPreflightFailure(e,t)}_preflightVersionsDiffer(){return this._editorRuntimeOrThrow()._preflightVersionsDiffer()}async _copyPreflightDiagnostics(){return this._editorRuntimeOrThrow()._copyPreflightDiagnostics()}_checkOptimizeGeometry(e){return this._editorRuntimeOrThrow()._checkOptimizeGeometryImpl(e)}_checkSpacePhysicalGeometry(e,t,i){return this._editorRuntimeOrThrow()._checkSpacePhysicalGeometryImpl(e,t,i)}_optimizeReferenceContext(e){return this._editorRuntimeOrThrow()._optimizeReferenceContext(e)}_previewAlignDialog(e){return this._editorRuntimeOrThrow()._previewAlignDialog(e)}async _runAlignToGrid(){return this._editorRuntimeOrThrow()._runAlignToGrid()}async _undoPlanOptimization(){return this._editorRuntimeOrThrow()._undoPlanOptimization()}async _runBackupExport(){return this._editorRuntimeOrThrow()._runBackupExport()}async _pickBackupImport(e){return this._editorRuntimeOrThrow()._pickBackupImport(e)}async _setBackupDuplicatePolicy(e){return this._editorRuntimeOrThrow()._setBackupDuplicatePolicy(e)}async _applyBackupImport(){return this._editorRuntimeOrThrow()._applyBackupImport()}_renderBackupExportDialog(){return this._editorRuntimeOrThrow()._renderBackupExportDialog()}_renderBackupImportDialog(){return this._editorRuntimeOrThrow()._renderBackupImportDialog()}_setFillColor(e,t){return this._editorRuntimeOrThrow()._setFillColor(e,t)}async _saveSettingsDialog(){return this._editorRuntimeOrThrow()._saveSettingsDialog()}_boolInput(e,t,i=!1){return this._editorRuntimeOrThrow()._boolInput(e,t,i)}_rangeInput(e,t,i,n,r,o=!1,s){return this._editorRuntimeOrThrow()._rangeInput(e,t,i,n,r,o,s)}_renderColorRow(e,t){return this._editorRuntimeOrThrow()._renderColorRow(e,t)}get _glowRadiusCm(){const e=Number(this._settings.glow_radius_cm);return Number.isFinite(e)&&e>0?e:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const e=this._glowRadiusCm;return this._imperial?String(Math.round(e/30.48*10)/10):String(e/100)}_glowTransition(e,t){return Im(this._glowRuntimeState,this._glowRuntimeHost,e,t)}_forgetGlowSource(e){Em(this._glowRuntimeState,this._glowRuntimeHost,e)}_forgetGlowSpace(e){Nm(this._glowRuntimeState,this._glowRuntimeHost,e)}_warnGlowGeometryFallback(e,t,i,n){Bm(this._glowRuntimeState,e,t,i,n)}_lightBarriers(e,t){const i=zm({rawSpaceConfig:this._curSpaceCfg,space:e,openings:this._openingsR,cellCm:this._cellCm,gridPitch:this._gridPitch,openingAmount:e=>this._openingAmt(e)}),n=`${e.id}|${i.fingerprint}`,r=_g(this._lightBarrierPool,n);if(r.hit)return this._lightBarrierCache=r.value,r.value.value;const o=Am({space:e,revision:i,walls:this._spaceWalls,zeroWalls:this._zeroWalls(),wallKeyPitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:_f,sharedWallGeometry:this._wallUnionGeometry(),physicalBodies:(t,i)=>(this._lightPhysicalBodiesCache?.key!==i&&(this._lightPhysicalBodiesCache={key:i,all:oa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,t).all}),this._lightPhysicalBodiesCache.all)}),s={key:n,value:o};return gg(this._lightBarrierPool,n,s,8),this._lightBarrierCache=s,o}_renderGlowLayer(e,t){const i=this._fillColors,n=this._glowRadiusCm/this._cellCm*this._gridPitch,r=e.rooms.map(e=>({r:e,poly:Ht(e)})).filter(e=>!!e.poly),o=r.filter(({r:e})=>bn(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const s=this._lightBarriers(e,r),a=Dm({hass:this._renderPlanHass,devices:this._renderDevices,virtualLights:this._virtualLights,spaceId:e.id,defaultColor:i.glow_light.c,paletteAlpha:i.glow_light.a,defaultRadiusUnits:n,cellCm:this._cellCm,gridPitch:this._gridPitch,position:e=>this._pos(e)}),l=[],c=new Set;for(const t of a){const{key:i,pos:n}=t;if(c.add(i),Fm(n,s)){this._forgetGlowSource(i);continue}const o=this._glowTransition(i,!!t.appearance);if(!o)continue;t.appearance&&this._glowLastAppearance.set(i,t.appearance);const a=this._glowLastAppearance.get(i);if(!a)continue;let h=null;const d=`${e.id}|${s.fingerprint}|${n.x.toFixed(4)},${n.y.toFixed(4)}|${t.radius.toFixed(4)}`,u=Rm(this._glowRuntimeState,d);u.hit?h=u.value:(h=Pm({spaceId:e.id,source:n,radius:t.radius,scene:s,polygons:r.map(({r:e,poly:t})=>({room:e,poly:t})),onBoundsFailure:(t,i)=>this._warnGlowGeometryFallback(e.id,s.fingerprint,t,i)}),Tm(this._glowRuntimeState,d,h)),l.push({key:i,sourceEid:t.sourceEid,domId:o.domId,entering:o.entering,leaving:o.leaving,pos:n,c:a.c,alpha:a.alpha,geometry:h,r:t.radius})}if(Hm(this._glowRuntimeState,this._glowRuntimeHost,e.id,c),!l.length)return j``;const h=this._spaceWalls,d=o.length===r.length?[]:this._openCuts(),u=this._wallUnionGeometry()?.roomGeom,p=o.length===r.length?null:o.map(({r:t,poly:i})=>{const n=h.length&&t.id&&this._innerRoomContour(e,t.id,d,u)||i,o=this._cleanFloor(t,n,e).path,s=ci(n,r.filter(e=>e.r!==t).map(e=>e.poly)),a=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||a(n),...s.map(a)].join(" ")}),m=this._viewOr(this._baseVb()),_=this._stageEl?.clientWidth&&m.w?this._stageEl.clientWidth/m.w:1,g=!this._pinchStart&&!this._panStart&&!this._cameraTransition.active,f=qm(this._glowRuntimeState,_,g);return Wm({spots:l,enabledClip:p,feather:f.feather,featherEnabled:f.enabled,screenBlend:this._glowScreenBlend})}_renderAlignDialog(){return this._editorRuntimeOrThrow()._renderAlignDialog()}_renderSettingsDialog(){return this._editorRuntimeOrThrow()._renderSettingsDialog()}_renderSupportDialog(){return this._editorRuntimeOrThrow()._renderSupportDialog()}_rulesSet(e){return this._editorRuntimeOrThrow()._rulesSet(e)}async _saveRules(){return this._editorRuntimeOrThrow()._saveRules()}_renderRulesDialog(){return this._editorRuntimeOrThrow()._renderRulesDialog()}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(uf,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>{const n=Math.round(100*e[t]);return W` + `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,n=t?t.bgColor||"":Gi(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||n}_stageBgHex(){const e=this._stageEl;if(e){const t=getComputedStyle(e).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(t)return"#"+t.slice(1,4).map(e=>(+e).toString(16).padStart(2,"0")).join("")}return"#111111"}_preflightDiagnostics(e,t){return this._editorRuntimeOrThrow()._preflightDiagnostics(e,t)}_reportPreflightFailure(e,t){return this._editorRuntimeOrThrow()._reportPreflightFailure(e,t)}_preflightVersionsDiffer(){return this._editorRuntimeOrThrow()._preflightVersionsDiffer()}async _copyPreflightDiagnostics(){return this._editorRuntimeOrThrow()._copyPreflightDiagnostics()}_checkOptimizeGeometry(e){return this._editorRuntimeOrThrow()._checkOptimizeGeometryImpl(e)}_checkSpacePhysicalGeometry(e,t,i){return this._editorRuntimeOrThrow()._checkSpacePhysicalGeometryImpl(e,t,i)}_optimizeReferenceContext(e){return this._editorRuntimeOrThrow()._optimizeReferenceContext(e)}_previewAlignDialog(e){return this._editorRuntimeOrThrow()._previewAlignDialog(e)}async _runAlignToGrid(){return this._editorRuntimeOrThrow()._runAlignToGrid()}async _undoPlanOptimization(){return this._editorRuntimeOrThrow()._undoPlanOptimization()}async _runBackupExport(){return this._editorRuntimeOrThrow()._runBackupExport()}async _pickBackupImport(e){return this._editorRuntimeOrThrow()._pickBackupImport(e)}async _setBackupDuplicatePolicy(e){return this._editorRuntimeOrThrow()._setBackupDuplicatePolicy(e)}async _applyBackupImport(){return this._editorRuntimeOrThrow()._applyBackupImport()}_renderBackupExportDialog(){return this._editorRuntimeOrThrow()._renderBackupExportDialog()}_renderBackupImportDialog(){return this._editorRuntimeOrThrow()._renderBackupImportDialog()}_setFillColor(e,t){return this._editorRuntimeOrThrow()._setFillColor(e,t)}async _saveSettingsDialog(){return this._editorRuntimeOrThrow()._saveSettingsDialog()}_boolInput(e,t,i=!1){return this._editorRuntimeOrThrow()._boolInput(e,t,i)}_rangeInput(e,t,i,n,r,o=!1,s){return this._editorRuntimeOrThrow()._rangeInput(e,t,i,n,r,o,s)}_renderColorRow(e,t){return this._editorRuntimeOrThrow()._renderColorRow(e,t)}get _glowRadiusCm(){const e=Number(this._settings.glow_radius_cm);return Number.isFinite(e)&&e>0?e:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const e=this._glowRadiusCm;return this._imperial?String(Math.round(e/30.48*10)/10):String(e/100)}_glowTransition(e,t){return Nm(this._glowRuntimeState,this._glowRuntimeHost,e,t)}_forgetGlowSource(e){Hm(this._glowRuntimeState,this._glowRuntimeHost,e)}_forgetGlowSpace(e){Lm(this._glowRuntimeState,this._glowRuntimeHost,e)}_warnGlowGeometryFallback(e,t,i,n){Wm(this._glowRuntimeState,e,t,i,n)}_lightBarriers(e,t){const i=Pm({rawSpaceConfig:this._curSpaceCfg,space:e,openings:this._openingsR,cellCm:this._cellCm,gridPitch:this._gridPitch,openingAmount:e=>this._openingAmt(e)}),n=`${e.id}|${i.fingerprint}`,r=yg(this._lightBarrierPool,n);if(r.hit)return this._lightBarrierCache=r.value,r.value.value;const o=Fm({space:e,revision:i,walls:this._spaceWalls,zeroWalls:this._zeroWalls(),wallKeyPitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:yf,sharedWallGeometry:this._wallUnionGeometry(),physicalBodies:(t,i)=>(this._lightPhysicalBodiesCache?.key!==i&&(this._lightPhysicalBodiesCache={key:i,all:sa(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,t).all}),this._lightPhysicalBodiesCache.all)}),s={key:n,value:o};return bg(this._lightBarrierPool,n,s,8),this._lightBarrierCache=s,o}_renderGlowLayer(e,t){const i=this._fillColors,n=this._glowRadiusCm/this._cellCm*this._gridPitch,r=e.rooms.map(e=>({r:e,poly:Ht(e)})).filter(e=>!!e.poly),o=r.filter(({r:e})=>bn(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const s=this._lightBarriers(e,r),a=Am({hass:this._renderPlanHass,devices:this._renderDevices,virtualLights:this._virtualLights,spaceId:e.id,defaultColor:i.glow_light.c,paletteAlpha:i.glow_light.a,defaultRadiusUnits:n,cellCm:this._cellCm,gridPitch:this._gridPitch,position:e=>this._pos(e)}),l=[],c=new Set;for(const t of a){const{key:i,pos:n}=t;if(c.add(i),Im(n,s)){this._forgetGlowSource(i);continue}const o=this._glowTransition(i,!!t.appearance);if(!o)continue;t.appearance&&this._glowLastAppearance.set(i,t.appearance);const a=this._glowLastAppearance.get(i);if(!a)continue;let h=null;const d=`${e.id}|${s.fingerprint}|${n.x.toFixed(4)},${n.y.toFixed(4)}|${t.radius.toFixed(4)}`,u=Dm(this._glowRuntimeState,d);u.hit?h=u.value:(h=Om({spaceId:e.id,source:n,radius:t.radius,scene:s,polygons:r.map(({r:e,poly:t})=>({room:e,poly:t})),onBoundsFailure:(t,i)=>this._warnGlowGeometryFallback(e.id,s.fingerprint,t,i)}),zm(this._glowRuntimeState,d,h)),l.push({key:i,sourceEid:t.sourceEid,domId:o.domId,entering:o.entering,leaving:o.leaving,pos:n,c:a.c,alpha:a.alpha,geometry:h,r:t.radius})}if(Bm(this._glowRuntimeState,this._glowRuntimeHost,e.id,c),!l.length)return j``;const h=this._spaceWalls,d=o.length===r.length?[]:this._openCuts(),u=this._wallUnionGeometry()?.roomGeom,p=o.length===r.length?null:o.map(({r:t,poly:i})=>{const n=h.length&&t.id&&this._innerRoomContour(e,t.id,d,u)||i,o=this._cleanFloor(t,n,e).path,s=ci(n,r.filter(e=>e.r!==t).map(e=>e.poly)),a=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||a(n),...s.map(a)].join(" ")}),m=this._viewOr(this._baseVb()),_=this._stageEl?.clientWidth&&m.w?this._stageEl.clientWidth/m.w:1,g=!this._pinchStart&&!this._panStart&&!this._cameraTransition.active,f=jm(this._glowRuntimeState,_,g);return Um({spots:l,enabledClip:p,feather:f.feather,featherEnabled:f.enabled,screenBlend:this._glowScreenBlend})}_renderAlignDialog(){return this._editorRuntimeOrThrow()._renderAlignDialog()}_renderSettingsDialog(){return this._editorRuntimeOrThrow()._renderSettingsDialog()}_renderSupportDialog(){return this._editorRuntimeOrThrow()._renderSupportDialog()}_rulesSet(e){return this._editorRuntimeOrThrow()._rulesSet(e)}async _saveRules(){return this._editorRuntimeOrThrow()._saveRules()}_renderRulesDialog(){return this._editorRuntimeOrThrow()._renderRulesDialog()}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(gf,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>{const n=Math.round(100*e[t]);return W`
    {const i=Number(e.target.value);Number.isFinite(i)&&this._saveKioskScale({[t]:i/100})}} aria-label=${i} /> @@ -5154,7 +5157,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef .request=${this._dangerConfirm.request} .token=${this._dangerConfirm.token} @hp-confirm-decision=${this._onDangerConfirmDecision}> - `:V}_renderRoot(e){return W`${e}${this._renderDangerConfirm()}`}render(){const e=this._renderBody();return e===V?e:this._renderRoot(e)}_renderBody(){if(!this._config||!this.hass)return V;const e=this._syncDangerConfirmLocaleGate();if("cold"===e)return du();if("warm"===e)return U;const t=!!this._importDialog||!(!this._spaceDialog||!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)),i="view"!==this._mode||!!(this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._partitionDeleteDialog||this._roomDeleteDialog||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog&&!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)||this._deviceInbox||this._markerDialog||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._backupExportDialog||this._backupImportDialog||this._kioskDialog||this._vacFit||this._vacCalConfirm);t&&!this._onboardingRuntime&&this._ensureOnboardingRuntime(),i&&!this._editorRuntime&&this._ensureEditorRuntime();const n=this._model,r=this.houseplanDiagnostics(),o=this._fixedFloorState(n);if("pending"===o.kind)return W` + `:V}_renderRoot(e){return W`${e}${this._renderDangerConfirm()}`}render(){const e=this._renderBody();return e===V?e:this._renderRoot(e)}_renderBody(){if(!this._config||!this.hass)return V;const e=this._terminalFrame;if(this._terminalFrame=0,1===e)return U;const t=this._syncDangerConfirmLocaleGate();if("cold"===t)return pu();if("warm"===t)return U;const i=!!this._importDialog||!(!this._spaceDialog||!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)),n="view"!==this._mode||!!(this._roomDialog||this._mergeDialog||this._openingDialog||this._physicalDialog||this._partitionDeleteDialog||this._roomDeleteDialog||this._decorTextDialog||this._decorShapeDialog||this._backdropDialog||this._decorEraseConfirm||this._spaceDialog&&!this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)||this._deviceInbox||this._markerDialog||this._rulesDialog||this._settingsDialog||this._supportDialog||this._alignDialog||this._backupExportDialog||this._backupImportDialog||this._kioskDialog||this._vacFit||this._vacCalConfirm);i&&!this._onboardingRuntime&&this._ensureOnboardingRuntime(),n&&!this._editorRuntime&&this._ensureEditorRuntime();const r=this._model,o=this._renderLife.diagnostics(this.hass,this._markers,e=>this._bindingStatus(e)),s=this._fixedFloorState(r);if("pending"===s.kind)return W`
    ${this._config.title||this._t("card.title")}
    @@ -5162,25 +5165,25 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef

    ${this._t("fixed_floor.loading")}

    - `;if("invalid"===o.kind)return W``;if("invalid"===s.kind)return W` + data-fixed-floor-reason=${s.reason}>
    ${this._config.title||this._t("card.title")}
    -
    `;if(!n.length)return W``;if(!r.length)return W` + data-ha-registry-access=${o.registry.access} + data-ha-disabled-bindings=${o.bindings.ha_disabled} + data-ha-unverified-bindings=${o.bindings.unverified}>
    ${this._config.title||this._t("card.title")}
    @@ -5195,16 +5198,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef ${this._spaceDialog&&(this._onboardingRuntime||this._editorRuntime)?this._renderSpaceDialog():V} ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():V} ${this._toast?W``:V} -
    `;const s=this._spaceModel();if(!s)return V;const a="valid"===o.kind?[s]:n,l=s.vb,c=this._effectiveProjection();this._renderProjection=c,this._labs.active.length&&function(){const e=Sg,t=e.alpha?e.active.join(","):"";if(!t||t===Cg)return;Cg=t;const i=new Map(bg.map(e=>[e.id,e])),n=e.active.map(e=>`${e} (#${i.get(e).issue})`).join(", ");console.info(`HOUSEPLAN ALPHA: hp_alpha=1; ${n}`)}();const h="iso"===c,d="devices"===this._mode&&this._showAll,u=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||d)),p=this._renderDeviceSnapshot,m=this._spaceDisplayForRender(),_=h?this._isoDecorationLayers(m):null,g=_?this._isoOpeningPanels(_):[],f=this._resolvedRoomFills(s,m),v=this._resolvedGlowBase(s,m,f),y=m.showLqi??this._config.show_signal??!0,b=this._config.icon_size??2.5,w=b>8?2.5:b,k=vp(w),x=this._viewOr(this._baseVb()),$=this._floorView(x),S=this._editing?"":this._stageBg(m),M=this._opMeasureView,C=this._decorMeasure,R=this._bdLive,T=this._editorRuntime?._furnLive()??null,D="view"===this._mode?this._editorChromeMode:this._mode,z=this._roomHoverPaths(s),A=s.bg?this._display(s.bg.href):"",P=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,F=this._modeTransitionVisual,O=this._dayCycleState(),I=F?.viewWeight??("view"===this._mode?1:0),E=this._modeTransition.state?.from.presentedMode,N=!this._markup||!!F&&("view"===F.presentedMode||"devices"===F.presentedMode||"view"===E||"devices"===E),H=F?.stageColor||S,L=F?.sceneBrightness??1;return W` +
    `;const a=this._spaceModel();if(!a)return V;const l="valid"===s.kind?[a]:r,c=a.vb,h=this._effectiveProjection();this._renderProjection=h,this._labs.active.length&&function(){const e=Tg,t=e.alpha?e.active.join(","):"";if(!t||t===zg)return;zg=t;const i=new Map(Sg.map(e=>[e.id,e])),n=e.active.map(e=>`${e} (#${i.get(e).issue})`).join(", ");console.info(`HOUSEPLAN ALPHA: hp_alpha=1; ${n}`)}();const d="iso"===h,u="devices"===this._mode&&this._showAll,p=this._renderDevices.filter(e=>e.space===a.id&&(!e.hidden||u)),m=this._renderDeviceSnapshot,_=this._spaceDisplayForRender(),g=d?this._isoDecorationLayers(_):null,f=g?this._isoOpeningPanels(g):[],v=this._resolvedRoomFills(a,_),y=this._resolvedGlowBase(a,_,v),b=_.showLqi??this._config.show_signal??!0,w=this._config.icon_size??2.5,k=w>8?2.5:w,x=bp(k),S=this._viewOr(this._baseVb()),$=this._floorView(S),M=this._editing?"":this._stageBg(_),C=this._opMeasureView,R="view"===this._mode?this._editorChromeMode:this._mode,T=this._roomHoverPaths(a),D=a.bg?this._display(a.bg.href):"",z=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,A=this._modeTransitionVisual,P=this._dayCycleState(),F=A?.viewWeight??("view"===this._mode?1:0),O=this._modeTransition.state?.from.presentedMode,I=!this._markup||!!A&&("view"===A.presentedMode||"devices"===A.presentedMode||"view"===O||"devices"===O),E=A?.stageColor||M,N=A?.sceneBrightness??1;return W` this._notePointer(e)} @pointerdown=${this._touchGestureGuard} @pointermove=${this._touchGestureGuard} @@ -5219,7 +5222,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef ${this._config.title||this._t("card.title")}
    this._tabPointerMove(e)}> - ${a.map(e=>W``)}
    `:V} - ${this._t("count.devices",{n:u.filter(e=>!e.hidden).length})} + ${this._t("count.devices",{n:p.filter(e=>!e.hidden).length})} - ${this._labsIso&&"view"===this._mode&&!this._kiosk?W``:V}
    @@ -5274,19 +5277,19 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef `:V}
    ${this._canEdit&&!this._kiosk?W`
    - ${this._editorRuntime?"plan"===D?this._renderMarkupBar():"devices"===D?this._renderDevicesBar():this._renderDecorBar():V} + style=${A?`opacity:${A.toolbarContentOpacity}`:V}> + ${this._editorRuntime?"plan"===R?this._renderMarkupBar():"devices"===R?this._renderDevicesBar():this._renderDecorBar():V}
    `:V}
    -
    this._markupClick(e)} @wheel=${e=>this._onWheel(e)} @pointerdown=${e=>{this._notePointer(e),this._stagePointerDown(e)}} @@ -5294,141 +5297,123 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef @pointerleave=${e=>this._stagePointerLeave(e)} @pointerup=${e=>this._stagePointerUp(e)} @pointercancel=${e=>this._stagePointerCancel(e)}> - ${nr(O,I)} + ${nr(P,F)} ${this._editorRuntime?this._renderEditorSecondary():V}
    - ${h&&_?.structural?j` + ${d&&g?.structural?j``:V} - - + + ${""} - ${this._wallHatchDefs(m.color)}${j`${this._paperShapes(s.rooms).map(e=>"path"in e?j``:"poly"in e?j``:j``)}`} - ${this._editing?this._renderMarkupDefs(l):V} + ${this._wallHatchDefs(_.color)}${j`${this._paperShapes(a.rooms).map(e=>"path"in e?j``:"poly"in e?j``:j``)}`} + ${this._editing?this._renderMarkupDefs(c):V} ${""} - ${this._editing&&!this._markup&&this._gridLevels()?j``:V} - ${s.bg&&A?j`this._onBackdropLoaded(s.bg.href,A)} - transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:V} + ${this._editing&&!this._markup&&this._gridLevels()?j``:V} + ${a.bg&&D?j`this._onBackdropLoaded(a.bg.href,D)} + transform=${a.bg.angle?`rotate(${a.bg.angle} ${a.bg.x+a.bg.w/2} ${a.bg.y+a.bg.h/2})`:V} @dblclick=${e=>this._openBackdropDialog(e)} preserveAspectRatio="none" />`:V} - ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Ht(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||m.showBorders).map(n=>{let o="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=yn(m.fill,n);if(!this._markup&&(m.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${m.color}`,`--room-stroke-op:${m.showBorders?m.opacity:0}`);const t=f.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),a=e.join(";")}let c;const h=e=>{this._roomTipEnabledForPointer(e)&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",y?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n),!0))},d=e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})},u=r(n),p=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),_=u&&!p?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return u.some((e,i)=>Mn(t,[e[0],e[1],...u[(i+1)%u.length]])<=.08*this._gridPitch)}):[],g=p?[]:t,v=_.concat(g);v.length&&(o+=" noedge");const b=this._spaceWalls.length&&n.id&&u&&this._innerRoomContour(s,n.id,e,i)||u,w=b?ci(b,(k=n,s.rooms.filter(e=>e!==k).map(r).filter(Boolean))):[];var k;const x=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",$=b?this._cleanFloor(n,b,s).path:"",S=n.id||V,M=n.area||V,C=$&&b?j`{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,r=e=>(n.has(e)||n.set(e,Ht(e)),n.get(e));return a.rooms.filter(e=>e.area||"view"===this._mode||this._markup||_.showBorders).map(n=>{let o="room "+(a.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(o+=" picked");let s="";const l=yn(_.fill,n);if(!this._markup&&(_.showBorders||"none"!==l)){o+=" styled";const e=[];e.push(`--room-stroke:${_.color}`,`--room-stroke-op:${_.showBorders?_.opacity:0}`);const t=v.byRoom.get(n)||null;t?(o+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),s=e.join(";")}let c;const h=e=>{this._roomTipEnabledForPointer(e)&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",b?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n),!0))},d=e=>{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:a.id,room:n},this._syncLiveHover())},u=r(n),p=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),m=u&&!p?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return u.some((e,i)=>Mn(t,[e[0],e[1],...u[(i+1)%u.length]])<=.08*this._gridPitch)}):[],g=p?[]:t,f=m.concat(g);f.length&&(o+=" noedge");const y=this._spaceWalls.length&&n.id&&u&&this._innerRoomContour(a,n.id,e,i)||u,w=y?ci(y,(k=n,a.rooms.filter(e=>e!==k).map(r).filter(Boolean))):[];var k;const x=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",S=y?this._cleanFloor(n,y,a).path:"",$=n.id||V,M=n.area||V,C=S&&y?j`this._clearTransientHover()}>`:w.length&&b?j`this._clearTransientHover()}>`:w.length&&y?j`this._clearTransientHover()}>`:b&&b!==u?j`this._clearTransientHover()}>`:y&&y!==u?j`this._clearTransientHover()}>`:n.poly?j`this._clearTransientHover()}>`:n.poly?j`this._clearTransientHover()}>`:j`this._clearTransientHover()}>`:j`this._clearTransientHover()}>`,R=v.length&&u?mn(u,v,.02*this._gridPitch):null,T=R?j`this._clearTransientHover()}>`,R=f.length&&u?mn(u,f,.02*this._gridPitch):null,T=R?j``:V;return j`${C}${T}`})})()} - ${this._renderRoomHoverFill(z)} - ${this._renderOpeningTunnelFills(s,f)} - ${this._renderGlowBaseRooms(s,v)} - ${this._renderOpeningTunnelFills(s,v,"glow-base")} + style=${this._markup?V:`stroke:${_.color};stroke-opacity:${_.showBorders?_.opacity:0}`}>`:V;return j`${C}${T}`})})()} + ${this._renderRoomHoverFill(T)} + ${this._renderOpeningTunnelFills(a,v)} + ${this._renderGlowBaseRooms(a,y)} + ${this._renderOpeningTunnelFills(a,y,"glow-base")} ${""} - ${m.hideDecor&&"decor"!==this._mode?V:this._renderDecorLayer()} - ${N?this._renderGlowLayer(s,m):V} - ${this._renderSunRays(s)} + ${_.hideDecor&&"decor"!==this._mode?V:this._renderDecorLayer()} + ${I?this._renderGlowLayer(a,_):V} + ${this._renderSunRays(a)} ${this._editing?j`${this._renderAlignGuides()}`:V} + opacity="${A?.editorWeight??1}">${this._renderAlignGuides()}`:V} ${this._markup?j`${this._renderMarkupLayer(l)}`:V} + opacity="${A?.editorWeight??1}">${this._renderMarkupLayer(c)}`:V} ${""} ${""} - ${this._editing?V:this._renderZeroWalls(m)} - ${this._renderWallBodies(m)} + ${this._editing?V:this._renderZeroWalls(_)} + ${this._renderWallBodies(_)} ${this._markup&&"resize"===this._tool?this._renderResizeMeasurements():V} - ${this._renderRoomHoverOutline(z)} + ${this._renderRoomHoverOutline(T)} ${""} - ${this._editing?this._renderZeroWalls(m):V} + ${this._editing?this._renderZeroWalls(_):V} ${this._markup?j`${this._renderHiddenWallDiagnosticOverlay()}`:V} + opacity="${A?.editorWeight??1}">${this._renderHiddenWallDiagnosticOverlay()}`:V} ${this._markup?j`${this._renderOpeningPlacementPreview()}`:V} - ${M?this._renderOpeningDimensionGuides(M):V} - ${M?.guide?this._renderOpeningCenterTick(M.guide):V} + opacity="${A?.editorWeight??1}">${this._renderOpeningPlacementPreview()}`:V} + ${C?this._renderOpeningDimensionGuides(C):V} + ${C?.guide?this._renderOpeningCenterTick(C.guide):V} ${""} ${""} ${this._markup?j`${this._renderActiveChainInk()}`:V} - ${this._markup?j`${this._renderPlanSnapOverlay()}`:V} - ${m.hideOpenings&&!this._markup||_&&!_.floorSymbols?V:this._renderOpenings(m)} + opacity="${A?.editorWeight??1}">${this._renderActiveChainInk()}`:V} + ${this._markup?j`${this._renderPlanSnapOverlay()}`:V} + ${_.hideOpenings&&!this._markup||g&&!g.floorSymbols?V:this._renderOpenings(_)} ${this._renderWallThickUi()} - ${this._markup&&"resize"===this._tool?this._renderResizeLayer(x):V} + ${this._markup&&"resize"===this._tool?this._renderResizeLayer(S):V} ${""} - ${this._renderBackdropFrame(x)} - ${this._renderTextFrame(x)} + ${this._renderBackdropFrame(S)} + ${this._renderTextFrame(S)} + - ${h&&_?.structural?j` - `:V}${B={hass:this.hass,settings:this._settings,devices:this._renderDevices,registry:this._haRegistry,currentSpace:s.id,viewKey:x,view:"view"===this._mode,kiosk:this._kiosk},B.view&&!B.kiosk&&!0===B.hass?.user?.is_admin&&z_(B.settings).enabled?(import("./hp-zigbee-topology-overlay-B7l7_FNi.js"),W``):V} + ${this._renderIsoWalls(g,f)} + `:V}${H={hass:this.hass,settings:this._settings,devices:this._renderDevices,registry:this._haRegistry,currentSpace:a.id,viewKey:S,view:"view"===this._mode,kiosk:this._kiosk},H.view&&!H.kiosk&&!0===H.hass?.user?.is_admin&&O_(H.settings).enabled?(import("./hp-zigbee-topology-overlay-CnCZWJj6.js"),W``):V} ${""} -
    - ${u.map(e=>this._renderDevice(e,x,y))} - ${this._renderVacuums(this._renderVacuumDevices,x,s.id)} - ${this._renderVacFit(x)} - ${this._renderOpeningLocks(x)} - ${m.showNames||this._markup?s.rooms.map(e=>this._renderRoomLabel(e,s,x,m)):V} - ${this._markup?s.rooms.map(e=>this._renderRoomGear(e,s,x)):V} +
    + ${p.map(e=>this._renderDevice(e,S,b))} + ${this._renderVacuums(this._renderVacuumDevices,S,a.id)} + ${this._renderVacFit(S)} + ${this._renderOpeningLocks(S)} + ${_.showNames||this._markup?a.rooms.map(e=>this._renderRoomLabel(e,a,S,_)):V} + ${this._markup?a.rooms.map(e=>this._renderRoomGear(e,a,S)):V}
    - ${this._measureAnchor?W`
    ${this._renderMeasureLabel(x)}
    `:V} - ${this._resize?.liveLabels?W`
    ${this._resize?.liveLabels?.map(e=>W`
    ${e.text}
    `)}
    `:V} - ${M?W`
    ${M.labels.map(e=>W`
    ${e.text}
    `)}
    `:V} - ${this._wallDialog?W`
    ${this._renderWallThickDialog()}
    `:V} - ${C?W`
    ${C.text}
    `:V} - ${T?W`
    ${T.map(e=>W`
    ${e.text}
    `)}
    `:V} - ${R?W`
    ${R.text}
    `:V} +
    + ${this._wallDialog?W`
    ${this._renderWallThickDialog()}
    `:V}
    - ${this._zoom>1?W`
    ${Math.round(100*this._zoom)}%
    `:V} + ${this._renderFarHint()} ${this._renderHomeArrow()} ${this._renderEditorRuntimeLoading()} @@ -5480,13 +5465,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef ${this._backupExportDialog&&this._editorRuntime?this._renderBackupExportDialog():V} ${this._backupImportDialog&&this._editorRuntime?this._renderBackupImportDialog():V} ${this._importDialog&&this._onboardingRuntime?this._renderImportDialog():V} - ${this._tip?W`
    - ${this._tip.title}${this._tip.meta?W`${this._tip.meta}`:V} - ${null!=this._tip.temp?W`${this._t("tip.temp_avg")} ${this._tip.temp}°`:V} - ${null!=this._tip.hum?W`${this._t("tip.hum_avg")} ${this._tip.hum}%`:V} - ${null!=this._tip.lqi?W`${this._t("tip.lqi")} - ${this._tip.lqi}`:V} -
    `:V} + ${this._kiosk&&!this._hasFixedFloor&&this._kioskDots&&this._model.length>1?W`
    ${this._model.map(e=>W``)}
    `:V} @@ -5520,16 +5499,16 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef `:V} ${this._toast?W``:V} - `;var B}_vacCandidateStatus(e,t,i=this._planHass){const n=this._bindingStatus("entity:"+e);if("ha_disabled"===n.kind)return"disabled";if("orphaned"===n.kind)return"missing";if("unverified"===n.kind)return"unverified";const r=i?.states?.[e];return"unavailable"===r?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,n]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const r=ic(e,n,t[e]);r&&i.push(r)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const n=e.marker?.vacuum,r="string"==typeof n?.source&&!!n.source,o=new Set(e.entities||[]),s=new Set(o);r&&s.add(n.source);const a=i?.entities||{},l=[];for(const e of s){const t=i?.states?.[e],o=ic(e,t,a[e]);o?l.push(o):r&&e===n.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:a[e]?.platform?String(a[e].platform):null,category:e.startsWith("camera.")?"camera":"partial",hasPosition:!1,hasRooms:!1,hasPath:!1,hasMapId:!1,score:0})}if(t&&this._vacAllCameraCache?.devId===e.id){const e=new Set(l.map(e=>e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Vh(this._planHass,this._devices,null,this._virtualLights)){const i=rp(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:ap(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=c_(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),d_(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Vh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==ki(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&h_(a,s,o,window.clearTimeout.bind(window)):(a=c_(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==ki(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=u_(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===ki(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=ac(this.hass.states[i||""]?.state),r=Ql(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==pc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=sc(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=sc(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){const n=this._vacEntity(e),r=n?i?.states?.[n]?.attributes?.selected_map:null;return o=t.mapId,s=r,"default"!==o?o:null!=s?String(s):"default";var o,s}_vacObservedMapId(e,t,i=this._planHass){const n=Ql(i?.states?.[t]?.attributes);return n?this._vacMapId(e,n,i):void 0}_vacSaveMatrix(e,t,i,n,r=""){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n,r)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):V}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_vacTrailPathD(e,t){const i=e.map(e=>e.map(([e,i])=>ql(t,e,i))),n=function(e,t,i=console.warn){if(!Number.isFinite(t)||t<=0)return[];const n=[];let r=0;for(const i of e){if(!i.every(Zl)){r++;continue}const e=i.filter((e,t)=>0===t||!Jl(e,i[t-1]));if(e.length<2)continue;const o=[{kind:"move",point:e[0]}];if(2!==e.length){for(let i=1;i0&&i(`[houseplan] vacuum trail: ${r} segment(s) dropped — non-finite point (check map calibration)`),n}(i,this._cmToUnits(17.5)),r=e=>{const t=this._scenePoint(e);return`${t[0].toFixed(1)} ${t[1].toFixed(1)}`};return n.map(e=>e.map(e=>"move"===e.kind?`M ${r(e.point)}`:"line"===e.kind?`L ${r(e.point)}`:`Q ${r(e.control)} ${r(e.point)}`).join(" ")).join(" ")}_renderVacuums(e,t,i){if(this._markup||"decor"===this._mode)return V;const n=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,r=this._vacJumpOnce||n!==this._vacViewKey;this._vacViewKey=n,this._vacJumpOnce=!1;const o=[],s=[];for(const n of e){if(n.hidden||!this._isVacDev(n))continue;if("static_icon"===ki(n.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${n.id}`),a=e?.source??this._vacSource(n,this._renderPlanHass);if(!a)continue;const l=e?.telemetry??Ql(this._renderPlanHass?.states[a]?.attributes);if(!l)continue;const c=e?.routes??fc(n.id,n.marker?.vacuum??null,n.space,a),h=e?.resolution??yc({routes:c,observed:bc(c,[a],e=>this._vacObservedMapId(n,e)),spaceIds:new Set(this._model.map(e=>e.id))}),d=e?.server??this._vacSrvTrails[n.id],u=kc({resolution:h,routes:c,renderSpace:i,rootSource:n.marker?.vacuum?.source??null,serverCurrent:d?.current??null,serverPrevious:d?.previous??null,explicitRoutes:Array.isArray(n.marker?.vacuum?.map_routes)});if(!u.live&&!u.previous)continue;const p=u.live,m=e?.runtime??this._vacRt.get(n.id),_=m?.moving??!1,g=pc(n.marker?.vacuum),f="always"===g||"cleaning"===g&&_,v=d,y=u.currentRunMatches&&Array.isArray(v?.current?.points)?v.current:null,b=u.previous&&Array.isArray(v?.previous?.points)?v.previous:null;if("always"===g&&b&&u.previous){const e=Kl(b.points),t=this._vacTrailPathD(e,u.previous);t&&s.push(j``)}if(f&&p){const e=Yl(l,y,m?.trail||[]),t=!_||"integration"!==e.source&&"server"!==e.source?e.path:Xl(e.path);if(t.length){const e=this._vacTrailPathD(t,p);e&&s.push(j``);const i=t[t.length-1];if(_&&i?.length>=2){const e=i[i.length-1],[t,r]=ql(p,e[0],e[1]),o=this._scenePoint([t,r]),a=o[0].toFixed(1),l=o[1].toFixed(1);s.push(j``)}}}if(!_||!l.pos||!p)continue;const[w,k]=ql(p,l.pos.x,l.pos.y),x=this._scenePoint([w,k]),$=(x[0]-t.x)/t.w*100,S=(x[1]-t.y)/t.h*100,M=m&&m.lastTs>0&&Date.now()-m.lastTs>6e4,C=n.marker?.icon||n.icon||"mdi:robot-vacuum";o.push(W`
    e.entityId));for(const t of this._vacAllCameraCache.candidates)e.has(t.entityId)||l.push(t)}const c={};for(const e of l)c[e.entityId]=this._vacCandidateStatus(e.entityId,e,i);return function(e,t,i,n){const r="string"==typeof e&&e.length>0,o=new Set(t),s=Array.from(i).sort((t,i)=>r&&t.entityId===e?-1:r&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(r){const t=s.find(t=>t.entityId===e);return{entityId:e,status:n[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:s}}const a=s.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===n[e.entityId]);return a?{entityId:a.entityId,status:"ok",pinned:!1,candidates:s}:{entityId:null,status:"none",pinned:!1,candidates:s}}(n?.source,o,l,c)}_vacSource(e,t=this._planHass){if(!1===e.marker?.vacuum?.live)return null;const i=this._vacSourceResolution(e,!1,t);return"ok"===i.status||"unsupported"===i.status?i.entityId:null}_vacEntity(e){return e.primary?.startsWith("vacuum.")?e.primary:(e.entities||[]).find(e=>e.startsWith("vacuum."))||null}_isVacDev(e){return!!this._vacEntity(e)}_activitySourceKey(e){return this._activitySnapshot(e).sourceKey}_activitySnapshot(e,t=Kh(this._planHass,this._devices,null,this._virtualLights)){const i=sp(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:cp(this._planHass,e,i)}}_stampActivity(e,t,i){let n=this._activityRt.get(e);n||(n=d_(i||"",[]),this._activityRt.set(e,n)),null!=i&&(n.sources=i),p_(n,t,Date.now(),window.clearTimeout.bind(window),e=>window.setTimeout(()=>this.requestUpdate(),e))}_syncActivityRuntime(){const e=new Map;if(!this.hass)return e;if(!1===this._config?.live_states){for(const e of this._activityRt.values())clearTimeout(e.timer);return this._activityRt.clear(),e}const t=new Set,i=Kh(this._planHass,this._devices,null,this._virtualLights);for(const n of this._devices){if(n.hidden)continue;if("icon_ripple"!==ki(n.marker?.display))continue;t.add(n.id);const r=this._activitySnapshot(n,i);e.set(n.id,r);const{samples:o,sourceKey:s}=r;let a=this._activityRt.get(n.id);a?a.sources!==s&&u_(a,s,o,window.clearTimeout.bind(window)):(a=d_(s,o),this._activityRt.set(n.id,a))}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e));return e}_activityTick(){if(!this.hass)return;const e=this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;if("icon_ripple"!==ki(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:n,sourceKey:r}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==r)continue;const s=m_(o,n,window.clearTimeout.bind(window));s&&this._stampActivity(t.id,s,r)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===ki(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),n=lc(this.hass.states[i||""]?.state),r=ec(this.hass.states[t]?.attributes);let o=this._vacRt.get(e.id);o||(o={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(e.id,o)),n&&!o.moving&&(o.trail=[],o.lastPos=null);const s="never"!==mc(e.marker?.vacuum)&&!r?.path.length;!n&&o.moving&&(o.endedTs=Date.now(),s&&o.lastPos&&(o.trail=ac(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=n;const a=r?.pos;if(n&&a){const e=a.x+":"+a.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,s&&o.lastPos&&(o.trail=ac(o.trail,o.lastPos,40)),o.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(e){const t=this._serverCfg;if(!t)return null;t.markers=t.markers||[];const i=t.markers.find(t=>t.id===e.id);if(i)return i;if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return null;const n={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(n),n}_renderVacSection(e){return this._editorRuntimeOrThrow()._renderVacSection(e)}_vacMapId(e,t,i=this._planHass){const n=this._vacEntity(e),r=n?i?.states?.[n]?.attributes?.selected_map:null;return o=t.mapId,s=r,"default"!==o?o:null!=s?String(s):"default";var o,s}_vacObservedMapId(e,t,i=this._planHass){const n=ec(i?.states?.[t]?.attributes);return n?this._vacMapId(e,n,i):void 0}_vacSaveMatrix(e,t,i,n,r=""){return this._editorRuntimeOrThrow()._vacSaveMatrix(e,t,i,n,r)}_vacPlanRoomAnchors(e){return this._editorRuntimeOrThrow()._vacPlanRoomAnchors(e)}_vacAutoCalibrate(e){return this._editorRuntimeOrThrow()._vacAutoCalibrate(e)}_vacApplyCalibrationProposal(e){return this._editorRuntimeOrThrow()._vacApplyCalibrationProposal(e)}_vacStartFit(e){return this._editorRuntimeOrThrow()._vacStartFit(e)}_vacFitSave(){return this._editorRuntimeOrThrow()._vacFitSave()}_vacFitTurn(e){return this._editorRuntimeOrThrow()._vacFitTurn(e)}_vacGhostCentre(e){return this._editorRuntimeOrThrow()._vacGhostCentre(e)}_vacDelta(e,t,i){return this._editorRuntimeOrThrow()._vacDelta(e,t,i)}_vacFitPointer(e,t){return this._editorRuntimeOrThrow()._vacFitPointer(e,t)}_renderVacFit(e){return this._editorRuntime?this._editorRuntimeOrThrow()._renderVacFit(e):V}_vacRafLoop(){this._vacRaf=requestAnimationFrame(()=>{const e=this.renderRoot,t=this._stageEl,i=this._vacLastView,n=e?.querySelectorAll?.(".vacpuck")||[];if(!t||!i||!n.length)return void(this._vacRaf=0);const r=t.getBoundingClientRect();for(const t of n){const n=t.getAttribute("data-mid"),o=t.getBoundingClientRect(),s=i.x+(o.left+o.width/2-r.left)/r.width*i.w,a=i.y+(o.top+o.height/2-r.top)/r.height*i.h;for(const t of e.querySelectorAll(`line.tip[data-mid="${n}"]`))t.setAttribute("x2",s.toFixed(1)),t.setAttribute("y2",a.toFixed(1))}this._vacRafLoop()})}_vacTrailPathD(e,t){const i=e.map(e=>e.map(([e,i])=>Wl(t,e,i))),n=function(e,t,i=console.warn){if(!Number.isFinite(t)||t<=0)return[];const n=[];let r=0;for(const i of e){if(!i.every(Jl)){r++;continue}const e=i.filter((e,t)=>0===t||!Ql(e,i[t-1]));if(e.length<2)continue;const o=[{kind:"move",point:e[0]}];if(2!==e.length){for(let i=1;i0&&i(`[houseplan] vacuum trail: ${r} segment(s) dropped — non-finite point (check map calibration)`),n}(i,this._cmToUnits(17.5)),r=e=>{const t=this._scenePoint(e);return`${t[0].toFixed(1)} ${t[1].toFixed(1)}`};return n.map(e=>e.map(e=>"move"===e.kind?`M ${r(e.point)}`:"line"===e.kind?`L ${r(e.point)}`:`Q ${r(e.control)} ${r(e.point)}`).join(" ")).join(" ")}_renderVacuums(e,t,i){if(this._markup||"decor"===this._mode)return V;const n=this._space+"|"+t.x+"|"+t.y+"|"+t.w+"|"+t.h,r=this._vacJumpOnce||n!==this._vacViewKey;this._vacViewKey=n,this._vacJumpOnce=!1;const o=[],s=[];for(const n of e){if(n.hidden||!this._isVacDev(n))continue;if("static_icon"===ki(n.marker?.display))continue;const e=this._renderDeviceSnapshot?.facts.get(`vacuum:${n.id}`),a=e?.source??this._vacSource(n,this._renderPlanHass);if(!a)continue;const l=e?.telemetry??ec(this._renderPlanHass?.states[a]?.attributes);if(!l)continue;const c=e?.routes??vc(n.id,n.marker?.vacuum??null,n.space,a),h=e?.resolution??bc({routes:c,observed:wc(c,[a],e=>this._vacObservedMapId(n,e)),spaceIds:new Set(this._model.map(e=>e.id))}),d=e?.server??this._vacSrvTrails[n.id],u=xc({resolution:h,routes:c,renderSpace:i,rootSource:n.marker?.vacuum?.source??null,serverCurrent:d?.current??null,serverPrevious:d?.previous??null,explicitRoutes:Array.isArray(n.marker?.vacuum?.map_routes)});if(!u.live&&!u.previous)continue;const p=u.live,m=e?.runtime??this._vacRt.get(n.id),_=m?.moving??!1,g=mc(n.marker?.vacuum),f="always"===g||"cleaning"===g&&_,v=d,y=u.currentRunMatches&&Array.isArray(v?.current?.points)?v.current:null,b=u.previous&&Array.isArray(v?.previous?.points)?v.previous:null;if("always"===g&&b&&u.previous){const e=Yl(b.points),t=this._vacTrailPathD(e,u.previous);t&&s.push(j``)}if(f&&p){const e=Xl(l,y,m?.trail||[]),t=!_||"integration"!==e.source&&"server"!==e.source?e.path:Zl(e.path);if(t.length){const e=this._vacTrailPathD(t,p);e&&s.push(j``);const i=t[t.length-1];if(_&&i?.length>=2){const e=i[i.length-1],[t,r]=Wl(p,e[0],e[1]),o=this._scenePoint([t,r]),a=o[0].toFixed(1),l=o[1].toFixed(1);s.push(j``)}}}if(!_||!l.pos||!p)continue;const[w,k]=Wl(p,l.pos.x,l.pos.y),x=this._scenePoint([w,k]),S=(x[0]-t.x)/t.w*100,$=(x[1]-t.y)/t.h*100,M=m&&m.lastTs>0&&Date.now()-m.lastTs>6e4,C=n.marker?.icon||n.icon||"mdi:robot-vacuum";o.push(W`
    {e.stopPropagation();const t=this._vacEntity(n);t&&this._openMoreInfo(t)}}>
    `)}return this._vacLastView=t,o.length&&!this._vacRaf&&this._vacRafLoop(),o.length||s.length?W` - ${s.length?j`${s}`:V} - ${o}`:V}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,..._p(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Ju(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",zu(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),m=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return W`
    ${s}`:V} + ${o}`:V}_renderDevice(e,t,i=!0){const n=this._pos(e),r=this._scenePoint([n.x,n.y]),o=(r[0]-t.x)/t.w*100,s=(r[1]-t.y)/t.h*100,a=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${s}%`,...fp(a)],c=a.disabledReason,h=a.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=ep(a),u="view"===this._mode||"devices"===this._mode,p=[h,a.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==a.pulse.kind?this._t(`marker.pulse_a11y_${a.pulse.reason}`):"",a.valueFullText||a.valueText||"",Pu(a.valueBadge),null!=a.lqiText&&a.lqiBand?this._t(`marker.lqi_a11y_${a.lqiBand}`,{value:a.lqiText}):""].filter(Boolean).join(", "),m=[e.model,a.valueBadge?.fullText||"",null!=a.lqiText?"LQI "+a.lqiText:""].filter(Boolean).join(" · ");return W`
    this._clickDevice(t,e)} @keydown=${t=>this._keyDevice(t,e)} @@ -5555,10 +5534,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef @pointercancel=${t=>this._pointerCancel(t,e)} @lostpointercapture=${t=>this._pointerCancel(t,e)} > - ${fp(a,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:a.haDisabled?h:""})} + ${yp(a,{newDevice:this._newIds.has(e.id),newDeviceTitle:this._t("device.new"),disabledTitle:a.haDisabled?h:""})} ${this._vacRouteBadge(e)}
    `}_vacRouteBadge(e){if(!this._isVacDev(e)||e.hidden)return V;if("static_icon"===ki(e.marker?.display))return V;const t=this._renderDeviceSnapshot?.facts.get(`vacuum:${e.id}`),i=function(e,t){if(!t||!e)return null;switch(e.kind){case"unmapped":case"needs_calibration":case"ambiguous":case"missing_space":return e.kind;default:return null}}(t?.resolution,this._vacRt.get(e.id)?.moving??!1);if(!i)return V;const n=this._t(`vac.route_warn_${i}`);return W` - `}_roomArea(e){const t=Ht(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return zn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return pd(this._renderPlanHass,t,"temp",this._markers);const i=_d(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return pd(this._renderPlanHass,t,"hum",this._markers);const i=_d(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache,n=this._settings.exclude_integrations;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e&&i.ex===n)return i.m;const r=vd(t,this._iconRules,e,this._excluded);return this._climateCache={h:t,r:this._iconRules,mk:e,ex:n,m:r},r}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*_f,y:i.y*_f};const n=this._roomCenter(e),r=this._gridPitch;return{x:Ea(Ot(n[0],r)),y:Ea(Ot(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return V;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(W`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(W`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(W`${t}`)}if(n.labelLight){const t=(d=Vh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(W`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return W`
    `}_roomArea(e){const t=Ht(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const n=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,r=this._cleanFloor(e,n),o=this._cellCm/this._gridPitch;return zn(r.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;if(t)return _d(this._renderPlanHass,t,"temp",this._markers);const i=fd(this._spaceModel()?.id,e);return i?this._climate().get(i)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;if(t)return _d(this._renderPlanHass,t,"hum",this._markers);const i=fd(this._spaceModel()?.id,e);return i?this._climate().get(i)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache,n=this._settings.exclude_integrations;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e&&i.ex===n)return i.m;const r=bd(t,this._iconRules,e,this._excluded);return this._climateCache={h:t,r:this._iconRules,mk:e,ex:n,m:r},r}_resetRoomDialogFields(){return this._editorRuntimeOrThrow()._resetRoomDialogFields()}_openRoomEdit(e){return this._editorRuntimeOrThrow()._openRoomEdit(e)}_roomSettingsFromDialog(){return this._editorRuntimeOrThrow()._roomSettingsFromDialog()}_saveRoomEdit(){return this._editorRuntimeOrThrow()._saveRoomEdit()}_roomSrcCandidates(){return this._editorRuntimeOrThrow()._roomSrcCandidates()}_roomSrcLabel(e){return this._editorRuntimeOrThrow()._roomSrcLabel(e)}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*yf,y:i.y*yf};const n=this._roomCenter(e),r=this._gridPitch;return{x:Na(Ot(n[0],r)),y:Na(Ot(n[1],r))}}_labelDown(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelDown(e,t,i)}_labelMove(e,t,i){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelMove(e,t,i)}_labelUp(e){if(this._editorRuntime)return this._editorRuntimeOrThrow()._labelUp(e)}_labelScale(e){const t=this._layout["rl_"+(e.id||"")]?.k;return"number"==typeof t&&Number.isFinite(t)?Math.min(3,Math.max(.5,t)):1}_rlResizeDown(e,t,i){return this._editorRuntimeOrThrow()._rlResizeDown(e,t,i)}_rlResizeMove(e){return this._editorRuntimeOrThrow()._rlResizeMove(e)}_rlResizeUp(){return this._editorRuntimeOrThrow()._rlResizeUp()}_renderRoomGear(e,t,i){return this._editorRuntimeOrThrow()._renderRoomGear(e,t,i)}_renderRoomLabel(e,t,i,n){if(!e.name&&!this._markup)return V;const r=this._labelPos(e,t.id),o=this._scenePoint([r.x,r.y]),s=(o[0]-i.x)/i.w*100,a=(o[1]-i.y)/i.h*100,l=Math.min(1,n.opacity+.25),c=this._labelScale(e),h=[];if(n.labelTemp||n.labelHum||n.labelLqi&&e.area||n.labelLight){if(n.labelTemp){const t=this._roomTemp(e);null!=t&&h.push(W`${t}°`)}if(n.labelHum){const t=this._roomHum(e);null!=t&&h.push(W`${t}%`)}if(n.labelLqi&&e.area){const t=this._roomLqi(e.area);null!=t&&h.push(W`${t}`)}if(n.labelLight){const t=(d=Kh(this._renderPlanHass,this._renderDevices,e,this._virtualLights)).length?{on:d.filter(e=>e.on).length,total:d.length}:null;if(t){const e=0===t.on?this._t("roomcard.light_off"):t.on===t.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:t.on,total:t.total});h.push(W`${e}`)}}}var d;const u=!!e.area,p=!this._markup;return W`
    - ${this._fmtLen(t,i)} · ${s}°
    `}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=$n(e.a,e.b);return{x:o,y:s,on45:Sn(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${zn(hr(Math.abs(n-t),this._cellCm,this._gridPitch)*hr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._deviceDrag?.moved){const e=this._devices.find(e=>e.id===this._deviceDrag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=_f,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]:null;return Ut(e.type,t?.state,!!e.invert,t?.attributes?.current_position)}_planEntityAvailable(e){return!!e&&(!ad(this._fullRegistryHass,e,sd(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!ad(this._renderPlanHass,e,sd(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=ph(e)){return!!t&&"active"===wh(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e){const t=this._openingsR;if(!t.length)return j``;const i=this._spaceModel();if(!i)return j``;const n=e.color,r=this._spaceWalls,o=this._openCuts(),s=this._openingWallIndexFor(i,o).value;return j`${t.map(e=>{if(e.orphanReason)return j``}_renderLiveEditorMeasurements(e){const t=e,i=this._opMeasureView,n=this._decorMeasure,r=this._bdLive,o=this._editorRuntime?._furnLive()??null;return W` + ${this._measureAnchor?W`
    ${this._renderMeasureLabel(t)}
    `:V} + ${this._resize?.liveLabels?W`
    ${this._resize.liveLabels.map(e=>W`
    ${e.text}
    `)}
    `:V} + ${i?W`
    ${i.labels.map(e=>W`
    ${e.text}
    `)}
    `:V} + ${n?W`
    ${n.text}
    `:V} + ${o?W`
    ${o.map(e=>W`
    ${e.text}
    `)}
    `:V} + ${r?W`
    ${r.text}
    `:V} + `}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"===this._tool&&this._path.length&&!this._contourClosed?this._path[this._path.length-1]:"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:null}_renderMeasureLabel(e){const t=this._measureAnchor,i=this._cursorPt,n=(i[0]-e.x)/e.w*100,r=(i[1]-e.y)/e.h*100,o=Sn(t,i),s=Math.round(10*o)/10,a=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const n=Math.abs(t[0]-e[0]),r=Math.abs(t[1]-e[1]);if(!Number.isFinite(n)||!Number.isFinite(r)||Math.hypot(n,r)<=i)return!1;const o=Math.max(n,r,1);return n<=i*o||r<=i*o||Math.abs(n-r)<=i*o}(t,i,2e-4*this._gridPitch);return W`
    + ${this._fmtLen(t,i)} · ${s}°
    `}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[n,r]=e.b;if(Math.abs(t-n)<1e-6&&Math.abs(i-r)<1e-6)return null;const o=(t+n)/2,s=(i+r)/2;if("line"===e.kind){const t=Sn(e.a,e.b);return{x:o,y:s,on45:$n(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const a=this._fmtLen([t,i],[n,i]),l=this._fmtLen([n,i],[n,r]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(n-t)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(r-i)/2]);return{x:o,y:s,on45:!1,text:Math.abs(Math.abs(n-t)-Math.abs(r-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${a}`}}return{x:o,y:s,on45:!1,text:`${a} × ${l} · ${zn(hr(Math.abs(n-t),this._cellCm,this._gridPitch)*hr(Math.abs(r-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const e=this._drag.id.slice(3),t=this._spaceModel()?.rooms.find(t=>t.id===e);return t?(()=>{const e=this._labelPos(t,this._space);return[e.x,e.y]})():null}return null}if("devices"===this._mode&&this._deviceDrag?.moved){const e=this._devices.find(e=>e.id===this._deviceDrag.id);return e?(()=>{const t=this._pos(e);return[t.x,t.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const e=this._decorList.find(e=>e.id===this._decorMove.id);if(!e)return null;const t=yf,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){return this._editorRuntimeOrThrow()._alignCandidates()}_renderAlignGuides(){return this._editorRuntimeOrThrow()._renderAlignGuides()}_renderOpeningCenterTick(e){return this._editorRuntimeOrThrow()._renderOpeningCenterTick(e)}_renderOpeningDimensionGuides(e){return this._editorRuntimeOrThrow()._renderOpeningDimensionGuides(e)}_roomCenter(e){if(e.poly){const t=e.poly.length;return[e.poly.reduce((e,t)=>e+t[0],0)/t,e.poly.reduce((e,t)=>e+t[1],0)/t]}return[e.x+e.w/2,e.y+.1*Math.min(e.w,e.h)]}_openingAmt(e){const t=e.contact&&this._renderOpeningEntityAvailable(e.contact)?this._renderPlanHass.states[e.contact]:null;return Ut(e.type,t?.state,!!e.invert,t?.attributes?.current_position)}_planEntityAvailable(e){return!!e&&(!cd(this._fullRegistryHass,e,ld(this._markers))&&"active"===this._bindingStatus("entity:"+e).kind)}_renderEntityAvailable(e){return!!e&&(!cd(this._renderPlanHass,e,ld(this._markers))&&(!!this._renderPlanHass.entities?.[e]&&!!this._renderPlanHass.states?.[e]))}_openingEntityAvailable(e){return function(e,t,i=mh(e)){return!!t&&"active"===kh(e,`entity:${t}`,i).kind}(this.hass,e,this._haRegistry)}_renderOpeningEntityAvailable(e){return t=this._renderPlanHass,!!(i=e)&&!!t?.states?.[i];var t,i}_renderOpeningPlacementPreview(){return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview()}_renderOpenings(e,t){const i=t?this._openingsR.filter(e=>t.includes(e.id)):this._openingsR;if(!i.length)return j``;const n=this._spaceModel();if(!n)return j``;const r=e.color,o=this._spaceWalls,s=this._openCuts(),a=this._openingWallIndexFor(n,s).value;return j`${i.map(e=>{if(e.orphanReason)return j`{t.stopPropagation(),this._editOpening(e)}}> - + ! - `;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":n,o="gate"===e.type?!e.flip_v:e.flip_v,a=e.partitionHost||r.length||"gate"===e.type?this._openingFace(e,s,!!o):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:n,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=$u(l),u=ya(10,this._cellCm),p=ya(12,this._cellCm);return j``;const t=this._openingAmt(e),i=t>0&&!!e.contact&&this._renderOpeningEntityAvailable(e.contact)?"var(--hp-open)":r,n="gate"===e.type?!e.flip_v:e.flip_v,s=e.partitionHost||o.length||"gate"===e.type?this._openingFace(e,a,!!n):{ox:0,oy:0,cm:0,side:-1},l={type:e.type,length:e.rlen,angle:e.angle,amount:t,flipH:!!e.flip_h,flipV:!!e.flip_v,base:r,tone:i,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s},{half:c,outlineHalf:h,hitHalf:d}=Mu(l),u=ba(10,this._cellCm),p=ba(12,this._cellCm);return j` - ${Tu(l)} + ${zu(l)} + rx="${ba(6,this._cellCm)}"> this._opClick(t,e)} @@ -5600,7 +5595,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef @pointermove=${t=>this._opPointerMove(t,e)} @pointerup=${t=>this._opPointerUp(t,e)} @pointercancel=${t=>this._opPointerUp(t,e)}> - `})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return W`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=ya(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,m=(u[1]-e.y)/e.h*100;return W`
    `})}`}_renderOpeningLocks(e){const t=this._openingsR.filter(e=>!e.orphanReason&&("door"===e.type||"gate"===e.type)&&e.lock&&this._renderOpeningEntityAvailable(e.lock));if(!t.length)return W``;const i=this._spaceModel();if(!i)return W``;const n=this._openCuts(),r=this._openingWallIndexFor(i,n).value;return W`${t.map(t=>{const i=this._renderPlanHass.states[t.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),s=(t.angle+90)*Math.PI/180,a="gate"===t.type?this._openingFace(t,r,!t.flip_v):null,l=ba(16,this._cellCm),c=a?-l*a.side:l*(t.flip_v?-1:1),h=t.rx+Math.cos(s)*c,d=t.ry+Math.sin(s)*c,u=this._scenePoint([h,d]),p=(u[0]-e.x)/e.w*100,m=(u[1]-e.y)/e.h*100;return W`
    {"view"===this._mode&&(e.stopPropagation(),this._openingInfo=t)}}>
    - `}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Vh(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?Ai(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(dn).filter(e=>this._planEntityAvailable(e));return W``}_renderOpeningDialog(){return this._editorRuntimeOrThrow()._renderOpeningDialog()}_gridLevels(){return this._editorRuntimeOrThrow()._gridLevels()}_renderMarkupDefs(e){return this._editorRuntimeOrThrow()._renderMarkupDefs(e)}_renderPhysicalEditorLayer(){return this._editorRuntimeOrThrow()._renderPhysicalEditorLayer()}_renderHiddenWallDiagnosticOverlay(){return this._editorRuntimeOrThrow()._renderHiddenWallDiagnosticOverlay()}_renderPlanSnapOverlay(){return this._editorRuntimeOrThrow()._renderPlanSnapOverlay()}_syncPlanSnapActiveMarker(e){return this._editorRuntimeOrThrow()._syncPlanSnapActiveMarker(e)}_syncPlanSnapConflictMarkers(e){return this._editorRuntimeOrThrow()._syncPlanSnapConflictMarkers(e)}_planSnapPhysicalSegment(e){return this._editorRuntimeOrThrow()._planSnapPhysicalSegment(e)}_drawPreviewJoinPatchD(e,t){return this._editorRuntimeOrThrow()._drawPreviewJoinPatchD(e,t)}_renderMarkupLayer(e){return this._editorRuntimeOrThrow()._renderMarkupLayer(e)}_renderActiveChainInk(){return this._editorRuntimeOrThrow()._renderActiveChainInk()}_renderPartitionDeleteDialog(){return this._editorRuntimeOrThrow()._renderPartitionDeleteDialog()}_renderRoomDeleteDialog(){return this._editorRuntimeOrThrow()._renderRoomDeleteDialog()}_renderPhysicalDialog(){return this._editorRuntimeOrThrow()._renderPhysicalDialog()}_renderMarkupBar(){return this._editorRuntimeOrThrow()._renderMarkupBar()}_renderDevicesBar(){return this._editorRuntimeOrThrow()._renderDevicesBar()}_renderDeviceInbox(){return this._editorRuntimeOrThrow()._renderDeviceInbox()}_cardEntities(e){const t=this._planHass,i=[],n=new Set,r=e=>{if(!e||n.has(e)||!t.states[e])return;const r=t.entities[e];if("config"===r?.entity_category||"diagnostic"===r?.entity_category)return;n.add(e);const o=e.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(o)?i.push({eid:e,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(o)?i.push({eid:e,kind:"open"}):["sensor","binary_sensor","number","select"].includes(o)&&i.push({eid:e,kind:"value"})};for(const i of Kh(t,this._devices,null,this._virtualLights))if(i.device.id===e.id)for(const e of[...i.serviceEids,...i.stateEids])r(e);e.primary&&r(e.primary);for(const t of e.entities)r(t);return i.slice(0,12)}_cardToggle(e){const t=e.split(".")[0];"lock"!==t&&"alarm_control_panel"!==t&&this._planEntityAvailable(e)&&this.hass.callService("homeassistant","toggle",{entity_id:e}).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))}_renderInfoCard(){const e=this._infoCard,t=e.primary?this.hass.states[e.primary]:void 0,i=t?Ai(this.hass,e.primary)?.text??t.state:null,n=(e.controls??e.marker?.controls??[]).filter(dn).filter(e=>this._planEntityAvailable(e));return W`
    ${(()=>{const t=this._cardEntities(e);return t.length?W`
    ${t.map(({eid:e,kind:t})=>{const i=this.hass.states[e],n=this.hass.entities[e]?.name||i?.attributes?.friendly_name||e,r=i?Ai(this.hass,e)?.text??i.state:"",o="on"===i?.state||["open","unlocked","playing","cleaning"].includes(i?.state);return W`
    - + ${n} ${"toggle"===t?W``:"open"===t?W`
    - `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return qd({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){const i=this._planHass?.states?.[e]||this.hass?.states?.[e];try{return i&&"function"==typeof this.hass?.formatEntityState?this.hass.formatEntityState(i):t}catch{return t}}_toggleConfirmationStateText(e){const t=String(e.state||"unknown"),i=e.entityId?this._toggleStateText(e.entityId,t):t;if(i.trim().toLocaleLowerCase()!==t.trim().toLocaleLowerCase())return i;const n={on:"confirm.state_on",off:"confirm.state_off",open:"confirm.state_open",closed:"confirm.state_closed",opening:"confirm.state_opening",closing:"confirm.state_closing",unknown:"confirm.state_unknown"}[t];return n?this._t(n):t.replaceAll("_"," ").replaceAll("-"," ")}_toggleConfirmationLines(e){const t={"turn-on":"confirm.state_on","turn-off":"confirm.state_off",open:"confirm.state_open",close:"confirm.state_closed",stop:"confirm.state_stopped"};return function(e,t){if(!jd(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,n=i?e.targets.filter(e=>"on"===e.state).length:0,r=i?0===n?t.groupAllOff():t.groupCurrent(n,e.targets.length):t.state(e.targets[0]);let o;return o="toggle"===e.nextEffect?t.expectedByHa():i&&"turn-on"===e.nextEffect?t.groupAllOn():i&&"turn-off"===e.nextEffect?t.groupAllOff():t.effect(e.nextEffect),[t.current(r),t.expected(o),...e.skippedTargets.length?[t.unavailable(e.skippedTargets.length)]:[]]}(e,{state:e=>this._toggleConfirmationStateText(e),current:e=>this._t("confirm.current_state",{state:e}),expected:e=>this._t("confirm.expected_state",{state:e}),groupCurrent:(e,t)=>this._t("confirm.group_current",{on:e,total:t}),groupAllOn:()=>this._t("confirm.group_all_on"),groupAllOff:()=>this._t("confirm.group_all_off"),unavailable:e=>this._t("confirm.unavailable_targets",{count:e}),effect:e=>this._t(t[e]),expectedByHa:()=>this._t("confirm.expected_by_ha")})}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return W`
    + `}_markerValueBadgeFields(e){return this._editorRuntimeOrThrow()._markerValueBadgeFields(e)}_markerDraft(e){return this._editorRuntimeOrThrow()._markerDraft(e)}_markerPreviewDevice(e){return this._editorRuntimeOrThrow()._markerPreviewDevice(e)}_markerPreviewDevices(e){return this._editorRuntimeOrThrow()._markerPreviewDevices(e)}_toggleIntent(e,t=this._devices){return jd({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){return this._editorRuntimeOrThrow()._toggleIntentForDialog(e)}_toggleStateText(e,t){const i=this._planHass?.states?.[e]||this.hass?.states?.[e];try{return i&&"function"==typeof this.hass?.formatEntityState?this.hass.formatEntityState(i):t}catch{return t}}_toggleConfirmationStateText(e){const t=String(e.state||"unknown"),i=e.entityId?this._toggleStateText(e.entityId,t):t;if(i.trim().toLocaleLowerCase()!==t.trim().toLocaleLowerCase())return i;const n={on:"confirm.state_on",off:"confirm.state_off",open:"confirm.state_open",closed:"confirm.state_closed",opening:"confirm.state_opening",closing:"confirm.state_closing",unknown:"confirm.state_unknown"}[t];return n?this._t(n):t.replaceAll("_"," ").replaceAll("-"," ")}_toggleConfirmationLines(e){const t={"turn-on":"confirm.state_on","turn-off":"confirm.state_off",open:"confirm.state_open",close:"confirm.state_closed",stop:"confirm.state_stopped"};return function(e,t){if(!Vd(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,n=i?e.targets.filter(e=>"on"===e.state).length:0,r=i?0===n?t.groupAllOff():t.groupCurrent(n,e.targets.length):t.state(e.targets[0]);let o;return o="toggle"===e.nextEffect?t.expectedByHa():i&&"turn-on"===e.nextEffect?t.groupAllOn():i&&"turn-off"===e.nextEffect?t.groupAllOff():t.effect(e.nextEffect),[t.current(r),t.expected(o),...e.skippedTargets.length?[t.unavailable(e.skippedTargets.length)]:[]]}(e,{state:e=>this._toggleConfirmationStateText(e),current:e=>this._t("confirm.current_state",{state:e}),expected:e=>this._t("confirm.expected_state",{state:e}),groupCurrent:(e,t)=>this._t("confirm.group_current",{on:e,total:t}),groupAllOn:()=>this._t("confirm.group_all_on"),groupAllOff:()=>this._t("confirm.group_all_off"),unavailable:e=>this._t("confirm.unavailable_targets",{count:e}),effect:e=>this._t(t[e]),expectedByHa:()=>this._t("confirm.expected_by_ha")})}_toggleHintLines(e){return this._editorRuntimeOrThrow()._toggleHintLines(e)}_effectiveStoredTapAction(e,t){return this._editorRuntimeOrThrow()._effectiveStoredTapAction(e,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._editorRuntimeOrThrow()._effectiveMarkerTapAction(e,t)}_announceToggleDraft(e){return this._editorRuntimeOrThrow()._announceToggleDraft(e)}_valueBadgeForBinding(e,t){return this._editorRuntimeOrThrow()._valueBadgeForBinding(e,t)}_markerSpatialSource(e){return this._editorRuntimeOrThrow()._markerSpatialSource(e)}_markerAutoHasSpatialSource(e){return this._editorRuntimeOrThrow()._markerAutoHasSpatialSource(e)}_setMarkerLightRole(e){return this._editorRuntimeOrThrow()._setMarkerLightRole(e)}_controlRefInfo(e){return this._editorRuntimeOrThrow()._controlRefInfo(e)}_valueBadgeCandidateLabel(e){return this._editorRuntimeOrThrow()._valueBadgeCandidateLabel(e)}_controlCandidates(e){return this._editorRuntimeOrThrow()._controlCandidates(e)}_addControlRef(e,t){return this._editorRuntimeOrThrow()._addControlRef(e,t)}_setMarkerGlowMode(e){return this._editorRuntimeOrThrow()._setMarkerGlowMode(e)}_renderMarkerDialog(){return this._editorRuntimeOrThrow()._renderMarkerDialog()}_renderSpaceDialog(){return this._onboardingRuntime&&this._spaceDialog&&this._spaceDialogUsesOnboardingRuntime(this._spaceDialog.mode)?this._onboardingRuntime._renderSpaceDialog():this._editorRuntimeOrThrow()._renderSpaceDialog()}_renderMergeDialog(){return this._editorRuntimeOrThrow()._renderMergeDialog()}_renderCardPreview(e,t,i){const n=18*e;return W`
    ${this._t("preview.room_name")} @@ -5673,4 +5668,4 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef 45% · ${this._t("roomcard.light_partial",{on:1,total:3})} -
    `}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}kf.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_backdropGuard:{state:!0},_tapConfirm:{state:!0},_dangerConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_decorImagePalette:{state:!0},_decorAssetCatalog:{state:!0},_decorAssetBusy:{state:!0},_furnCategory:{state:!0},_furnPreviewInput:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_supportDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},kf.ZOOM_MAX=8,kf.ZOOM_MIN=1/3,kf.styles=[su,k_],customElements.get("houseplan-card")||customElements.define("houseplan-card",kf),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${tf} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{nu as $,V as A,wr as B,ai as C,li as D,U as E,Ct as F,hi as G,kl as H,Na as I,yl as J,gl as K,hs as L,Go as M,xa as N,sd as O,ad as P,Bt as Q,pn as R,Mn as S,qt as T,Fa as U,Aa as V,Ht as W,Zd as X,ou as Y,ds as Z,no as _,fc as a,Fp as a$,Yd as a0,Po as a1,ps as a2,Jd as a3,Ns as a4,Bs as a5,po as a6,Xt as a7,io as a8,vo as a9,Rl as aA,Da as aB,Tl as aC,Jo as aD,Is as aE,Io as aF,Vs as aG,ga as aH,Op as aI,Ks as aJ,us as aK,z_ as aL,Ki as aM,Qn as aN,Jn as aO,Zn as aP,Gi as aQ,ve as aR,xu as aS,eg as aT,uo as aU,Ea as aV,Ot as aW,It as aX,kr as aY,Ys as aZ,Ll as a_,xp as aa,$p as ab,ts as ac,Nr as ad,Ro as ae,Mo as af,Jr as ag,eo as ah,Fr as ai,Pp as aj,Do as ak,Pa as al,Co as am,Sa as an,Ma as ao,Ii as ap,Ei as aq,Di as ar,zi as as,Ta as at,am as au,cr as av,j as aw,om as ax,ll as ay,ml as az,dc as b,Oo as b$,to as b0,x_ as b1,cs as b2,Ps as b3,fs as b4,Ep as b5,ti as b6,Zt as b7,_i as b8,ma as b9,Sl as bA,gr as bB,Ml as bC,bl as bD,_l as bE,Cl as bF,xl as bG,$l as bH,em as bI,Fo as bJ,lo as bK,ya as bL,$u as bM,iu as bN,Zg as bO,tu as bP,qs as bQ,Vg as bR,Jg as bS,Sp as bT,jt as bU,Qd as bV,ru as bW,ui as bX,Qt as bY,Wt as bZ,ei as b_,ta as ba,ea as bb,zo as bc,Ao as bd,Os as be,Et as bf,il as bg,da as bh,ha as bi,Dn as bj,zn as bk,ri as bl,Zo as bm,Xo as bn,Hp as bo,Lp as bp,_r as bq,yr as br,vr as bs,hr as bt,sr as bu,lr as bv,pr as bw,$n as bx,or as by,ar as bz,W as c,qh as c$,gi as c0,an as c1,Wu as c2,ju as c3,Ah as c4,Nd as c5,ki as c6,Ci as c7,Tn as c8,wn as c9,rc as cA,pc as cB,Vl as cC,oc as cD,ec as cE,tc as cF,uc as cG,cc as cH,ql as cI,kn as cJ,xn as cK,Ut as cL,Gg as cM,Tu as cN,Ug as cO,rl as cP,ms as cQ,qo as cR,Wo as cS,fe as cT,fd as cU,Uu as cV,Vu as cW,ud as cX,Gd as cY,Gh as cZ,Vh as c_,nd as ca,Kp as cb,Qr as cc,od as cd,id as ce,Vi as cf,ji as cg,Wi as ch,wa as ci,Dp as cj,qi as ck,Bi as cl,Li as cm,Hi as cn,ba as co,jp as cp,Np as cq,ur as cr,Yi as cs,A_ as ct,qr as cu,er as cv,ye as cw,$e as cx,be as cy,Ql as cz,$_ as d,Fh as d0,dn as d1,zh as d2,rd as d3,ln as d4,cp as d5,Pu as d6,Wh as d7,jh as d8,Oh as d9,Pd as da,xi as db,wi as dc,Fu as dd,bi as de,ka as df,$i as dg,Si as dh,ro as di,Yt as dj,cd as dk,dd as dl,za as dm,le as dn,Bl as dp,bu as dq,hu as dr,_u as ds,du as dt,Ft as du,kf as dv,cm as e,hc as f,Il as g,S_ as h,hm as i,o as j,he as k,wu as l,Dt as m,_c as n,bc as o,su as p,mp as q,yc as r,Ni as s,ku as t,_p as u,zu as v,Ku as w,fp as x,D_ as y,ni as z}; +
    `}_renderRoomSource(e){return this._editorRuntimeOrThrow()._renderRoomSource(e)}_renderRoomDialog(){return this._editorRuntimeOrThrow()._renderRoomDialog()}}Mf.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_editorRuntimeLoadingVisible:{state:!0},_backdropGuard:{state:!0},_tapConfirm:{state:!0},_dangerConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_partitionDeleteDialog:{state:!0},_roomDeleteDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingPreset:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_decorImagePalette:{state:!0},_decorAssetCatalog:{state:!0},_decorAssetBusy:{state:!0},_furnCategory:{state:!0},_furnPreviewInput:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_vacAllCamerasFor:{state:!0},_vacCalConfirm:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomCustomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_deviceInbox:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_supportDialog:{state:!0},_alignDialog:{state:!0},_preflightClipboardFallback:{state:!0},_backupExportDialog:{state:!0},_backupImportDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0}},Mf.ZOOM_MAX=8,Mf.ZOOM_MIN=1/3,Mf.styles=[lu,S_],customElements.get("houseplan-card")||customElements.define("houseplan-card",Mf),window.customCards=window.customCards||[],window.customCards.find(e=>"houseplan-card"===e.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${sf} `,"background:#3ea6ff;color:#04121f;font-weight:700","");export{cd as $,V as A,Xu as B,gp as C,le as D,U as E,fp as F,Pu as G,yp as H,F_ as I,ni as J,wr as K,ai as L,li as M,Sa as N,Ct as O,hi as P,Oa as Q,xl as R,Ha as S,bl as T,fl as U,hs as V,Go as W,cs as X,Ps as Y,fs as Z,ld as _,vc as a,$u as a$,Bt as a0,pn as a1,Mn as a2,qt as a3,Pa as a4,Ht as a5,Qd as a6,au as a7,ds as a8,no as a9,Ei as aA,Di as aB,zi as aC,Da as aD,cm as aE,cr as aF,am as aG,cl as aH,_l as aI,Tl as aJ,za as aK,Dl as aL,Jo as aM,Is as aN,Io as aO,Gs as aP,fa as aQ,Ep as aR,Ys as aS,us as aT,O_ as aU,Ki as aV,Qn as aW,Jn as aX,Zn as aY,Gi as aZ,ve as a_,ou as aa,Zd as ab,Po as ac,ps as ad,eu as ae,Hs as af,qs as ag,Xt as ah,io as ai,vo as aj,$p as ak,Mp as al,ts as am,Nr as an,Ro as ao,Mo as ap,Jr as aq,eo as ar,Fr as as,Op as at,Do as au,Fa as av,Co as aw,Ma as ax,Ca as ay,Ii as az,uc as b,ui as b$,rg as b0,uo as b1,Na as b2,Ot as b3,It as b4,kr as b5,Xs as b6,Bl as b7,Ip as b8,to as b9,sr as bA,lr as bB,pr as bC,Sn as bD,or as bE,ar as bF,Ml as bG,gr as bH,Cl as bI,wl as bJ,gl as bK,Rl as bL,Sl as bM,$l as bN,im as bO,Fo as bP,Mu as bQ,ru as bR,tf as bS,nu as bT,Ws as bU,Xg as bV,nf as bW,Cp as bX,jt as bY,tu as bZ,su as b_,$_ as ba,Hp as bb,ti as bc,Zt as bd,_i as be,_a as bf,ia as bg,ta as bh,zo as bi,Ao as bj,Os as bk,Et as bl,nl as bm,ua as bn,da as bo,Dn as bp,zn as bq,ri as br,Zo as bs,Xo as bt,Bp as bu,qp as bv,_r as bw,yr as bx,vr as by,hr as bz,W as c,md as c$,Qt as c0,Wt as c1,ei as c2,Oo as c3,gi as c4,an as c5,Uu as c6,Vu as c7,Fh as c8,Ld as c9,ye as cA,Se as cB,be as cC,ec as cD,oc as cE,mc as cF,Gl as cG,sc as cH,tc as cI,ic as cJ,pc as cK,hc as cL,Wl as cM,kn as cN,xn as cO,Ut as cP,Zg as cQ,zu as cR,Yg as cS,ol as cT,ms as cU,qo as cV,Wo as cW,fe as cX,yd as cY,Gu as cZ,Ku as c_,ki as ca,Ci as cb,Tn as cc,wn as cd,od as ce,Xp as cf,Qr as cg,ad as ch,rd as ci,Vi as cj,ji as ck,Wi as cl,ka as cm,Ap as cn,qi as co,Bi as cp,Li as cq,Hi as cr,wa as cs,Vp as ct,Lp as cu,ur as cv,Yi as cw,I_ as cx,qr as cy,er as cz,M_ as d,Yd as d0,Yh as d1,Kh as d2,jh as d3,Ih as d4,dn as d5,Ph as d6,sd as d7,ln as d8,dp as d9,Ou as da,Uh as db,Vh as dc,Eh as dd,Od as de,xi as df,wi as dg,Iu as dh,bi as di,xa as dj,Si as dk,$i as dl,ro as dm,Yt as dn,dd as dp,pd as dq,Aa as dr,ql as ds,ku as dt,uu as du,fu as dv,pu as dw,Ft as dx,Mf as dy,dm as e,dc as f,El as g,C_ as h,um as i,Es as j,ao as k,xu as l,co as m,gc as n,wc as o,lo as p,ba as q,bc as r,Ni as s,po as t,o as u,he as v,j as w,Dt as x,lu as y,Su as z}; diff --git a/dist/houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js b/dist/houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js new file mode 100644 index 00000000..31ffd1a4 --- /dev/null +++ b/dist/houseplan-assets/houseplan-editor-runtime-C5_zNXOu.js @@ -0,0 +1,2939 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{e as t,i as e,E as s,s as i,a as o,n as a,f as n,b as r,l,o as h,r as c,A as d,c as p,d as u,g as _,h as m,D as g,w as f,N as b,j as v,k as y,m as $,p as w,q as k,t as x,u as S,v as M,x as D,y as C,z as P,B as I,C as T,F as z,G as R,H as A,I as F,J as N,K as H,L,M as E,O,P as B,Q as W,R as j,S as G,T as q,U,V as K,W as V,X as J,Y,Z as X,_ as Z,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as lt,a9 as ht,aa as ct,ab as dt,ac as pt,ad as ut,ae as _t,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as zt,ax as Rt,ay as At,az as Ft,aA as Nt,aB as Ht,aC as Lt,aD as Et,aE as Ot,aF as Bt,aG as Wt,aH as jt,aI as Gt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Xt,aQ as Zt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as le,a$ as he,b0 as ce,b1 as de,b2 as pe,b3 as ue,b4 as _e,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as ze,bn as Re,bo as Ae,bp as Fe,bq as Ne,br as He,bs as Le,bt as Ee,bu as Oe,bv as Be,bw as We,bx as je,by as Ge,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Xe,bG as Ze,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as ls,bR as hs,bS as cs,bT as ds,bU as ps,bV as us,bW as _s,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as zs,cd as Rs,ce as As,cf as Fs,cg as Ns,ch as Hs,ci as Ls,cj as Es,ck as Os,cl as Bs,cm as Ws,cn as js,co as Gs,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Xs,cw as Zs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as li,cH as hi,cI as ci,cJ as di,cK as pi,cL as ui,cM as _i,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di,c_ as Ci,c$ as Pi,d0 as Ii,d1 as Ti,d2 as zi,d3 as Ri,d4 as Ai,d5 as Fi,d6 as Ni,d7 as Hi,d8 as Li,d9 as Ei,da as Oi,db as Bi,dc as Wi,dd as ji,de as Gi,df as qi,dg as Ui,dh as Ki,di as Vi,dj as Ji,dk as Yi,dl as Xi,dm as Zi,dn as Qi,dp as to,dq as eo,dr as so}from"./houseplan-card-Csq_DR2Y.js";import{F as io,f as oo,p as ao,c as no,i as ro,e as lo,r as ho,a as co,b as po,s as uo,d as _o,t as mo}from"./backdrop-pick-CEZBr4qx.js";import{t as go,m as fo,T as bo}from"./zigbee-topology-D9L1ESh4.js";const vo={},yo=t(class extends e{constructor(){super(...arguments),this.ot=vo}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}});var $o={"gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.show_room_tooltip":"Show the room information window on hover","support.about_group":"About the card","support.guide_group":"Documentation","support.guide":"User guide","support.form_group":"Send a report or suggestion","support.contact":"Contact details (email/tg/WhatsApp), optional.","support.message":"Message","support.attach":"Attach anonymized information from your plan","support.attach_hint":"Includes only allowed plan geometry, settings and bounded runtime facts. Excludes names, Home Assistant IDs, live states, URLs, files, and your message/contact.","support.geometry_warning":"The package contains no names or Home Assistant IDs, but it does contain the exact geometry and dimensions of your home.","support.close_busy_title":"Stop the current operation?","support.close_busy_body":"House Plan is preparing or sending the report. Closing now stops waiting for the result.","support.building":"Preparing anonymized data…","support.preview_summary":"Package v{version} · {spaces} spaces · {size} KiB","support.prepared":"Snapshot prepared {n} min ago","support.show_data":"Show data","support.download":"Download JSON","support.refresh":"Refresh snapshot","support.privacy":"When you send, your message, optional contact and, if selected, exact geometry leave your Home Assistant through the House Plan project relay and are visible to maintainers. The project node keeps the report for up to 30 days. Network infrastructure sees your HA server address, but House Plan does not retain it raw; rate-limit and idempotency metadata is kept for up to 24 hours.","support.error_title":"The report was not sent","support.manual_recovery":"You can keep the message and attachment and continue manually:","support.copy_message":"Copy message","support.success":"Report sent: {id}. Save this number when contacting support.","support.copy_id":"Copy ID","support.sending":"Sending…","support.retry":"Retry","support.send":"Send","support.update_required":"Update the House Plan card and integration to compatible versions to send reports.","support.message_copied":"Message copied","support.id_copied":"Report ID copied","support.copy_failed":"Could not access the clipboard. Select and copy the text manually.","support.validation.message_required":"Enter a message.","support.validation.message_too_long":"The message must contain no more than 10,000 characters.","support.validation.contact_too_long":"Contact details must contain no more than 320 characters.","support.validation.preview_missing":"Wait for the attachment preview or turn the attachment off.","support.validation.preview_expired":"The attachment preview expired. Refresh the snapshot.","support.error.support_invalid_message":"Check the message and try again.","support.error.support_preview_expired":"The attachment preview expired or is no longer available. Refresh the snapshot.","support.error.support_package_too_large":"The anonymized package exceeds the 8 MiB limit.","support.error.support_rate_limited":"Too many reports or previews were requested. Try again later.","support.error.support_unavailable":"The private support service is temporarily unavailable. Your draft remains here.","support.error.support_rejected":"The report did not pass the support service validation.","support.error.unauthorized":"You do not have permission to send this report.","vac.routes_section":"Maps and floors","vac.routes_current_map":"Current map","vac.routes_no_map":"no telemetry","vac.routes_status":"Routing","vac.routes_state_ready":"map assigned and calibrated","vac.routes_state_needs_calibration":"map assigned, calibration needed","vac.routes_state_unmapped":"current map is not assigned to a floor","vac.routes_state_ambiguous":"two maps are indistinguishable — no floor is chosen","vac.routes_state_missing_space":"the map's space was deleted","vac.routes_state_none":"no map data","vac.routes_empty":"No map is assigned to a floor yet.","vac.route_map_default":"no map id","vac.route_active":"Active","vac.route_space":"Space","vac.route_space_none":"not selected","vac.route_status_ready":"ready","vac.route_status_needs_calibration":"needs calibration","vac.route_status_ambiguous":"ambiguous","vac.route_status_missing_space":"space deleted","vac.route_missing_space_group":"Deleted space","vac.route_add_current":"Add current map","vac.route_delete":"Remove map","vac.route_delete_title":"Remove the map from its floor?","vac.route_delete_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_delete_confirm":"Remove","vac.route_space_title":"Move the map to another space?","vac.route_space_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_space_confirm":"Move","vac.route_missing_space_hint":"One of the maps points at a deleted space: pick a new one or remove the map. Until then the robot is not drawn for that map.","vac.route_default_hint":"The source reports no stable map id. Several floors cannot be told apart without another source or the robot's own selected map.","vac.route_target_hint":"The dock stays in the “{space}” space regardless of the current map.","vac.route_add_source":"Add a map source","vac.route_add_source_hint":"For integrations that give each map its own camera. The “All cameras” section above widens this list.","vac.route_source_no_map":"no map reported"};const wo={en:$o,ru:{"gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.show_room_tooltip":"Показывать окно с информацией при наведении на комнату","support.about_group":"О карточке","support.guide_group":"Документация","support.guide":"Руководство пользователя","support.form_group":"Отправить репорт или предложение","support.contact":"Контакт для связи (email/tg/WhatsApp), необязательно.","support.message":"Сообщение","support.attach":"Прикрепить обезличенную информацию из вашего плана","support.attach_hint":"В пакет входят только разрешённые геометрия, настройки и ограниченные сведения о среде. Не входят имена, HA id, текущие состояния, URL, файлы, сообщение и контакт.","support.geometry_warning":"Пакет не содержит имён и HA id, но содержит точную геометрию и размеры дома.","support.close_busy_title":"Прервать текущую операцию?","support.close_busy_body":"House Plan подготавливает или отправляет репорт. При закрытии ожидание результата прекратится.","support.building":"Подготавливаем обезличенные данные…","support.preview_summary":"Пакет v{version} · пространств: {spaces} · {size} КиБ","support.prepared":"Снимок подготовлен {n} мин назад","support.show_data":"Показать данные","support.download":"Скачать JSON","support.refresh":"Обновить снимок","support.privacy":"При отправке сообщение, необязательный контакт и, если выбран пакет, точная геометрия покидают ваш Home Assistant через relay проекта House Plan и доступны мейнтейнерам. Узел проекта хранит репорт до 30 дней. Сетевая инфраструктура видит адрес сервера HA, но House Plan не сохраняет его в исходном виде; метаданные лимитов и идемпотентности хранятся до 24 часов.","support.error_title":"Репорт не отправлен","support.manual_recovery":"Сообщение и вложение можно сохранить и продолжить вручную:","support.copy_message":"Копировать сообщение","support.success":"Репорт отправлен: {id}. Сохраните номер для связи с поддержкой.","support.copy_id":"Копировать ID","support.sending":"Отправляем…","support.retry":"Повторить","support.send":"Отправить","support.update_required":"Обновите карточку и интеграцию House Plan до совместимых версий, чтобы отправлять репорты.","support.message_copied":"Сообщение скопировано","support.id_copied":"ID репорта скопирован","support.copy_failed":"Не удалось получить доступ к буферу обмена. Выделите и скопируйте текст вручную.","support.validation.message_required":"Введите сообщение.","support.validation.message_too_long":"Сообщение должно содержать не более 10 000 символов.","support.validation.contact_too_long":"Контакт должен содержать не более 320 символов.","support.validation.preview_missing":"Дождитесь предпросмотра вложения или отключите его.","support.validation.preview_expired":"Предпросмотр вложения истёк. Обновите снимок.","support.error.support_invalid_message":"Проверьте сообщение и повторите попытку.","support.error.support_preview_expired":"Предпросмотр вложения истёк или больше недоступен. Обновите снимок.","support.error.support_package_too_large":"Обезличенный пакет превышает лимит 8 МиБ.","support.error.support_rate_limited":"Запрошено слишком много репортов или предпросмотров. Повторите позже.","support.error.support_unavailable":"Закрытая служба поддержки временно недоступна. Черновик сохранён в этом окне.","support.error.support_rejected":"Репорт не прошёл проверку службы поддержки.","support.error.unauthorized":"У вас нет прав на отправку этого репорта.","vac.routes_section":"Карты и этажи","vac.routes_current_map":"Текущая карта","vac.routes_no_map":"источник молчит","vac.routes_status":"Маршрутизация","vac.routes_state_ready":"карта сопоставлена и откалибрована","vac.routes_state_needs_calibration":"карта сопоставлена, нужна калибровка","vac.routes_state_unmapped":"текущая карта не сопоставлена этажу","vac.routes_state_ambiguous":"две карты неразличимы — этаж не выбирается","vac.routes_state_missing_space":"пространство карты удалено","vac.routes_state_none":"нет данных о карте","vac.routes_empty":"Ни одна карта ещё не сопоставлена этажу.","vac.route_map_default":"без идентификатора","vac.route_active":"Текущая","vac.route_space":"Пространство","vac.route_space_none":"не выбрано","vac.route_status_ready":"готова","vac.route_status_needs_calibration":"нужна калибровка","vac.route_status_ambiguous":"неоднозначна","vac.route_status_missing_space":"пространство удалено","vac.route_missing_space_group":"Пространство удалено","vac.route_add_current":"Добавить текущую карту","vac.route_delete":"Удалить карту","vac.route_delete_title":"Удалить карту с этажа?","vac.route_delete_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_delete_confirm":"Удалить","vac.route_space_title":"Перенести карту в другое пространство?","vac.route_space_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_space_confirm":"Перенести","vac.route_missing_space_hint":"Одна из карт ссылается на удалённое пространство: выберите новое или удалите карту. Пока ссылка не исправлена, робот на этой карте не рисуется.","vac.route_default_hint":"Источник не сообщает стабильный идентификатор карты. Несколько этажей нельзя различить без другого источника или собственной карты робота.","vac.route_target_hint":"Док остаётся в пространстве «{space}» независимо от текущей карты.","vac.route_add_source":"Добавить источник карты","vac.route_add_source_hint":"Для интеграций, где у каждой карты своя камера. Список расширяется разделом «Все камеры» выше.","vac.route_source_no_map":"карта не читается"},de:{"gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.show_room_tooltip":"Rauminformationen beim Darüberfahren anzeigen","support.about_group":"Über die Karte","support.guide_group":"Dokumentation","support.guide":"Benutzerhandbuch","support.form_group":"Bericht oder Vorschlag senden","support.contact":"Kontakt (E-Mail/tg/WhatsApp), optional.","support.message":"Nachricht","support.attach":"Anonymisierte Informationen aus dem Plan anhängen","support.attach_hint":"Enthält nur erlaubte Plangeometrie, Einstellungen und begrenzte Laufzeitdaten. Namen, Home-Assistant-IDs, Live-Zustände, URLs, Dateien, Nachricht und Kontakt werden ausgeschlossen.","support.geometry_warning":"Das Paket enthält keine Namen oder Home-Assistant-IDs, aber die exakte Geometrie und die Maße Ihres Hauses.","support.close_busy_title":"Aktuellen Vorgang abbrechen?","support.close_busy_body":"House Plan bereitet den Bericht vor oder sendet ihn. Beim Schließen wird nicht weiter auf das Ergebnis gewartet.","support.building":"Anonymisierte Daten werden vorbereitet…","support.preview_summary":"Paket v{version} · {spaces} Bereiche · {size} KiB","support.prepared":"Schnappschuss vor {n} Min. erstellt","support.show_data":"Daten anzeigen","support.download":"JSON herunterladen","support.refresh":"Schnappschuss aktualisieren","support.privacy":"Beim Senden verlassen Ihre Nachricht, der optionale Kontakt und, falls ausgewählt, die exakte Geometrie Ihren Home Assistant über das House-Plan-Projektrelay und sind für die Maintainer sichtbar. Der Projektserver speichert den Bericht bis zu 30 Tage. Die Netzwerkinfrastruktur sieht die Adresse Ihres HA-Servers, House Plan speichert sie jedoch nicht im Rohformat; Limit- und Idempotenzdaten bleiben bis zu 24 Stunden erhalten.","support.error_title":"Der Bericht wurde nicht gesendet","support.manual_recovery":"Nachricht und Anhang können gespeichert und manuell weitergegeben werden:","support.copy_message":"Nachricht kopieren","support.success":"Bericht gesendet: {id}. Bewahren Sie diese Nummer für den Support auf.","support.copy_id":"ID kopieren","support.sending":"Wird gesendet…","support.retry":"Erneut versuchen","support.send":"Senden","support.update_required":"Aktualisieren Sie House-Plan-Karte und Integration auf kompatible Versionen, um Berichte zu senden.","support.message_copied":"Nachricht kopiert","support.id_copied":"Berichts-ID kopiert","support.copy_failed":"Kein Zugriff auf die Zwischenablage. Markieren und kopieren Sie den Text manuell.","support.validation.message_required":"Geben Sie eine Nachricht ein.","support.validation.message_too_long":"Die Nachricht darf höchstens 10.000 Zeichen enthalten.","support.validation.contact_too_long":"Der Kontakt darf höchstens 320 Zeichen enthalten.","support.validation.preview_missing":"Warten Sie auf die Vorschau oder deaktivieren Sie den Anhang.","support.validation.preview_expired":"Die Anhangsvorschau ist abgelaufen. Aktualisieren Sie den Schnappschuss.","support.error.support_invalid_message":"Prüfen Sie die Nachricht und versuchen Sie es erneut.","support.error.support_preview_expired":"Die Anhangsvorschau ist abgelaufen oder nicht mehr verfügbar. Aktualisieren Sie den Schnappschuss.","support.error.support_package_too_large":"Das anonymisierte Paket überschreitet das Limit von 8 MiB.","support.error.support_rate_limited":"Zu viele Berichte oder Vorschauen wurden angefordert. Versuchen Sie es später erneut.","support.error.support_unavailable":"Der private Supportdienst ist vorübergehend nicht verfügbar. Der Entwurf bleibt in diesem Fenster erhalten.","support.error.support_rejected":"Der Bericht hat die Prüfung des Supportdienstes nicht bestanden.","support.error.unauthorized":"Sie dürfen diesen Bericht nicht senden.","vac.routes_section":"Karten und Etagen","vac.routes_current_map":"Aktuelle Karte","vac.routes_no_map":"keine Telemetrie","vac.routes_status":"Zuordnung","vac.routes_state_ready":"Karte zugeordnet und kalibriert","vac.routes_state_needs_calibration":"Karte zugeordnet, Kalibrierung nötig","vac.routes_state_unmapped":"aktuelle Karte keiner Etage zugeordnet","vac.routes_state_ambiguous":"zwei Karten nicht unterscheidbar — keine Etage gewählt","vac.routes_state_missing_space":"der Raum der Karte wurde gelöscht","vac.routes_state_none":"keine Kartendaten","vac.routes_empty":"Noch keine Karte einer Etage zugeordnet.","vac.route_map_default":"ohne Karten-ID","vac.route_active":"Aktiv","vac.route_space":"Raum","vac.route_space_none":"nicht gewählt","vac.route_status_ready":"bereit","vac.route_status_needs_calibration":"Kalibrierung nötig","vac.route_status_ambiguous":"mehrdeutig","vac.route_status_missing_space":"Raum gelöscht","vac.route_missing_space_group":"Gelöschter Bereich","vac.route_add_current":"Aktuelle Karte hinzufügen","vac.route_delete":"Karte entfernen","vac.route_delete_title":"Karte von der Etage entfernen?","vac.route_delete_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_delete_confirm":"Entfernen","vac.route_space_title":"Karte in einen anderen Raum verschieben?","vac.route_space_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_space_confirm":"Verschieben","vac.route_missing_space_hint":"Eine Karte verweist auf einen gelöschten Raum: neuen wählen oder Karte entfernen. Bis dahin wird der Roboter für diese Karte nicht gezeichnet.","vac.route_default_hint":"Die Quelle meldet keine stabile Karten-ID. Mehrere Etagen lassen sich ohne andere Quelle oder die vom Roboter gewählte Karte nicht unterscheiden.","vac.route_target_hint":"Die Station bleibt unabhängig von der aktuellen Karte im Raum „{space}“.","vac.route_add_source":"Kartenquelle hinzufügen","vac.route_add_source_hint":"Für Integrationen, die jeder Karte eine eigene Kamera geben. Der Abschnitt „Alle Kameras“ oben erweitert diese Liste.","vac.route_source_no_map":"keine Karte gemeldet"},fr:{"gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.show_room_tooltip":"Afficher les informations de la pièce au survol","support.about_group":"À propos de la carte","support.guide_group":"Documentation d’aide","support.guide":"Guide utilisateur","support.form_group":"Envoyer un rapport ou une suggestion","support.contact":"Contact (e-mail/tg/WhatsApp), facultatif.","support.message":"Votre message","support.attach":"Joindre des informations anonymisées de votre plan","support.attach_hint":"Inclut uniquement la géométrie, les réglages autorisés et des données d’exécution limitées. Exclut les noms, identifiants Home Assistant, états en direct, URL, fichiers, message et contact.","support.geometry_warning":"Le paquet ne contient ni noms ni identifiants Home Assistant, mais il contient la géométrie et les dimensions exactes de votre logement.","support.close_busy_title":"Interrompre l’opération en cours ?","support.close_busy_body":"House Plan prépare ou envoie le rapport. Fermer maintenant arrête l’attente du résultat.","support.building":"Préparation des données anonymisées…","support.preview_summary":"Paquet v{version} · {spaces} espaces · {size} Kio","support.prepared":"Instantané préparé il y a {n} min","support.show_data":"Afficher les données","support.download":"Télécharger le JSON","support.refresh":"Actualiser l’instantané","support.privacy":"Lors de l’envoi, votre message, le contact facultatif et, si elle est sélectionnée, la géométrie exacte quittent votre Home Assistant via le relais du projet House Plan et sont visibles par les mainteneurs. Le serveur du projet conserve le rapport jusqu’à 30 jours. L’infrastructure réseau voit l’adresse de votre serveur HA, mais House Plan ne la conserve pas en clair ; les métadonnées de limitation et d’idempotence sont gardées jusqu’à 24 heures.","support.error_title":"Le rapport n’a pas été envoyé","support.manual_recovery":"Vous pouvez conserver le message et la pièce jointe puis continuer manuellement :","support.copy_message":"Copier le message","support.success":"Rapport envoyé : {id}. Conservez ce numéro pour contacter l’assistance.","support.copy_id":"Copier l’ID","support.sending":"Envoi…","support.retry":"Réessayer","support.send":"Envoyer","support.update_required":"Mettez à jour la carte et l’intégration House Plan vers des versions compatibles pour envoyer des rapports.","support.message_copied":"Message copié","support.id_copied":"ID du rapport copié","support.copy_failed":"Impossible d’accéder au presse-papiers. Sélectionnez et copiez le texte manuellement.","support.validation.message_required":"Saisissez un message.","support.validation.message_too_long":"Le message ne doit pas dépasser 10 000 caractères.","support.validation.contact_too_long":"Le contact ne doit pas dépasser 320 caractères.","support.validation.preview_missing":"Attendez l’aperçu de la pièce jointe ou désactivez-la.","support.validation.preview_expired":"L’aperçu de la pièce jointe a expiré. Actualisez l’instantané.","support.error.support_invalid_message":"Vérifiez le message et réessayez.","support.error.support_preview_expired":"L’aperçu de la pièce jointe a expiré ou n’est plus disponible. Actualisez l’instantané.","support.error.support_package_too_large":"Le paquet anonymisé dépasse la limite de 8 Mio.","support.error.support_rate_limited":"Trop de rapports ou d’aperçus ont été demandés. Réessayez plus tard.","support.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible. Le brouillon reste dans cette fenêtre.","support.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","support.error.unauthorized":"Vous n’êtes pas autorisé à envoyer ce rapport.","vac.routes_section":"Cartes et étages","vac.routes_current_map":"Carte actuelle","vac.routes_no_map":"aucune télémétrie","vac.routes_status":"Routage","vac.routes_state_ready":"carte assignée et calibrée","vac.routes_state_needs_calibration":"carte assignée, calibrage requis","vac.routes_state_unmapped":"carte actuelle non assignée à un étage","vac.routes_state_ambiguous":"deux cartes indiscernables — aucun étage choisi","vac.routes_state_missing_space":"l'espace de la carte a été supprimé","vac.routes_state_none":"aucune donnée de carte","vac.routes_empty":"Aucune carte n'est encore assignée à un étage.","vac.route_map_default":"sans identifiant","vac.route_active":"Active","vac.route_space":"Espace","vac.route_space_none":"non sélectionné","vac.route_status_ready":"prête","vac.route_status_needs_calibration":"calibrage requis","vac.route_status_ambiguous":"ambiguë","vac.route_status_missing_space":"espace supprimé","vac.route_missing_space_group":"Espace supprimé","vac.route_add_current":"Ajouter la carte actuelle","vac.route_delete":"Supprimer la carte","vac.route_delete_title":"Retirer la carte de son étage ?","vac.route_delete_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_delete_confirm":"Supprimer","vac.route_space_title":"Déplacer la carte vers un autre espace ?","vac.route_space_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_space_confirm":"Déplacer","vac.route_missing_space_hint":"Une carte pointe vers un espace supprimé : choisissez-en un autre ou supprimez la carte. D'ici là, le robot n'est pas dessiné pour cette carte.","vac.route_default_hint":"La source ne fournit pas d'identifiant de carte stable. Plusieurs étages ne peuvent pas être distingués sans une autre source ou la carte sélectionnée du robot.","vac.route_target_hint":"La station reste dans l'espace « {space} » quelle que soit la carte actuelle.","vac.route_add_source":"Ajouter une source de carte","vac.route_add_source_hint":"Pour les intégrations qui donnent à chaque carte sa propre caméra. La section « Toutes les caméras » ci-dessus élargit cette liste.","vac.route_source_no_map":"aucune carte signalée"}};function ko(t,e,s){return i(wo[t]?.[e]??$o[e]??e,s)}function xo(t,e,s,i,o){return{markerId:t,source:i,mapId:o,space:e.length?"":s}}function So(t,e,s){return{...t,space:s.has(e)?e:""}}function Mo(t,e=Math.random){const s=new Set(t);for(let t=0;t<64;t+=1){const t="vr_"+Math.floor(16777215*e()).toString(36).padStart(5,"0");if(!s.has(t))return t}return"vr_"+Date.now().toString(36)}function Do(t,e,s){return t.map(t=>t.id===e?{...t,calibration:s}:t)}const Co=new WeakMap,Po=(t,e)=>{const s=(t._serverCfg?.spaces||[]).find(t=>t?.id===e);return String(s?.name||s?.id||e)};function Io(t,e,s){const i=t.host,n=l(i.hass,i._config?.language),r=(t,e)=>ko(n,t,e),g=e.marker?.vacuum||{},f=Array.isArray(g.map_routes),b=i._vacSource(e)||"",v=o(e.id,g,e.space,b),y=(i._serverCfg?.spaces||[]).filter(t=>"string"==typeof t?.id&&!!t.id).map(t=>({id:t.id,name:String(t.name||t.id)})),$=new Set(y.map(t=>t.id)),w=h(v,[b],t=>i._vacObservedMapId(e,t)),k=c({routes:v,observed:w,spaceIds:$}),x="ready"===k.kind||"needs_calibration"===k.kind||"missing_space"===k.kind?k.route.id:"",S=b?w[b]:void 0,M=async s=>{let o=f?g.map_routes??null:null;if(!o&&(o=function(t,e,s,i){if(!s)return null;const o=t?.calibration;if(!o||"object"!=typeof o)return null;const n=new Set,r=[];for(const t of Object.keys(o)){const l=a(o[t]);if(!l)continue;const h=i(n);n.add(h),r.push({id:h,source:s,map_id:t,space:e,calibration:l})}return r.length?r:null}(g,e.space,b,t=>Mo(t))??[],!b&&Object.keys(g.calibration||{}).length))return!1;const n=s(o);return!!n&&(async s=>{const o=i._serverCfg;if(!o)return!1;const a=JSON.parse(JSON.stringify(o));a.markers=a.markers||[];let n=a.markers.find(t=>t.id===e.id);if(!n){if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return!1;n={id:e.id,binding:`${e.bindingKind}:${e.bindingRef}`,space:e.space||null,area:e.area||null,hidden:!!e.hidden},a.markers.push(n)}n.vacuum={...n.vacuum||{},map_routes:s},delete n.vacuum.calibration;const r=u(o,a,i._cfgContentFingerprint,i._cfgRev,_);i._serverCfg=a,i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._saveConfigDebounced.pending()&&i._saveConfigDebounced.cancel();try{return await t._saveConfigNow(),!0}catch(t){return m(i,r,_),i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._showToast(i._t("toast.cfg_save_failed",{err:i._errText(t)})),!1}})(n)},D=s.candidates.filter(t=>t.entityId!==b&&!v.some(e=>e.source===t.entityId)),C=async(t,e)=>{if(!e||e===t.space)return;await i._confirmDanger({key:"vacuum_route_space",kind:"warning",title:r("vac.route_space_title"),message:r("vac.route_space_body"),objectName:t.map_id||r("vac.route_map_default"),confirmLabel:r("vac.route_space_confirm"),cancelLabel:i._t("btn.cancel")})&&await M(s=>function(t,e,s,i){return t.map(t=>t.id===e?{...t,id:i,space:s,calibration:null}:t)}(s,t.id,e,Mo(new Set(v.map(t=>t.id)))))},P=new Map(y.map((t,e)=>[t.id,e])),I=(t,e)=>t.map_id.localeCompare(e.map_id)||t.source.localeCompare(e.source)||t.id.localeCompare(e.id),T=v.filter(t=>$.has(t.space)).sort((t,e)=>(P.get(t.space)??0)-(P.get(e.space)??0)||I(t,e)),z=v.filter(t=>!$.has(t.space)).sort(I),R=[...T,...z],A=!!b&&void 0!==S&&!v.some(t=>t.source===b&&t.map_id===S);let F=Co.get(t);const N=F?.source||"",H=F?.mapId||"",L=!!F&&v.some(t=>t.source===N&&t.map_id===H);F&&(F.markerId!==e.id||i._vacObservedMapId(e,F.source)!==F.mapId||L&&!F.saving)&&(Co.delete(t),F=void 0),F?.space&&!$.has(F.space)&&(F=So(F,"",$),Co.set(t,F));const E=s=>p` +
  • +
    + ${s.map_id||r("vac.route_map_default")} + ${s.id===x?p`${r("vac.route_active")}`:d} +
    + ${s.source} +
    ${(t=>$.has(t.space)?t.calibration?"ambiguous"===k.kind&&k.routeIds.includes(t.id)?r("vac.route_status_ambiguous"):r("vac.route_status_ready"):r("vac.route_status_needs_calibration"):r("vac.route_status_missing_space"))(s)}
    + +
    + + +
    +
  • `,O=`vacroute-missing-${e.id.replace(/[^a-zA-Z0-9_-]/g,"-")}`;return p` + +
    +
    +
    ${r("vac.routes_current_map")}${void 0===S?r("vac.routes_no_map"):S||r("vac.route_map_default")}
    +
    ${r("vac.routes_status")}${r(`vac.routes_state_${k.kind}`)}
    +
    + ${T.length||F?p`
      + ${F?p` +
    • +
      ${F.mapId||r("vac.route_map_default")}
      + ${F.source} + +
      + + +
      +
    • `:d} + ${T.map(E)} +
    `:d} + ${z.length?p` +
    +

    + ${r("vac.route_missing_space_group")} +

    +
      ${z.map(E)}
    +
    `:d} + ${R.length||F?d:p`
    ${r("vac.routes_empty")}
    `} +
    + +
    + ${D.length?p`
    + ${r("vac.route_add_source")} +
    ${r("vac.route_add_source_hint")}
    +
    + ${D.map(s=>{const o=i._vacObservedMapId(e,s.entityId);return p``})} +
    +
    `:d} + ${R.some(t=>!$.has(t.space))?p` +
    ${r("vac.route_missing_space_hint")}
    `:d} + ${v.some(t=>"default"===t.map_id)?p` +
    ${r("vac.route_default_hint")}
    `:d} + ${R.length?p`
    ${r("vac.route_target_hint",{space:Po(i,e.space)})}
    `:d} +
    `}const To=new WeakMap;function zo(t,e,s){let i=To.get(t);i||(i=new Map,To.set(t,i));const o=i.get(e);if(o)return void(o.run=s);const a={run:s};i.set(e,a),queueMicrotask(()=>{i.get(e)===a&&(i.delete(e),a.run())})}function Ro(t,e){const s=To.get(t),i=s?.get(e);i&&(s.delete(e),i.run())}function Ao(t,e){const s=To.get(t);if(!s)return;const i=e?[...s].filter(([t])=>t===e):[...s];for(const[t]of i)s.delete(t);s.size||To.delete(t)}const Fo=new WeakMap,No=new Set(["_cursorPt","_opMeasure","_physicalDrag","_physicalRotate","_decorDraft","_dtBox","_dtDrag","_bdDrag","_furnPreviewInput","_layout"]),Ho=new Set(["_cursorPt","_opMeasure","_furnPreviewInput"]),Lo=new Set(["_physicalDrag","_physicalRotate","_decorDraft","_dtDrag","_bdDrag"]),Eo=t=>{let e=Fo.get(t);return e||(e={raf:0,hidden:[],transparent:[],dimmed:[]},Fo.set(t,e)),e},Oo=t=>!!("view"!==t._mode&&(t._deviceDrag||t._physicalDrag||t._physicalRotate||t._decorDraft||t._decorMove||t._dtDrag||t._bdDrag||t._opDrag||t._resize?.dragging));function Bo(t,e,s){const i=t;if(!i?.isConnected||"view"===i._mode)return!1;const o=void 0!==e&&No.has(e);if("_layout"===e&&!i._deviceDrag)return!1;if("_dtBox"===e&&!i._dtDrag)return!1;if(void 0!==e&&Lo.has(e)&&null==s&&null!=i[e]&&Oo(i))return!1;if(void 0!==e&&Lo.has(e)&&null==i[e])return!1;return!!(o&&(Ho.has(e)||Oo(i))||void 0===e&&Oo(i))&&(function(t){const e=Eo(t);if(e.raf||"function"!=typeof requestAnimationFrame)return;e.raf=requestAnimationFrame(()=>{e.raf=0,function(t){const e=t,s=e.renderRoot;if(!s)return;const i=Eo(e);Go(i),Ko(e,s);const o=s.querySelector("[data-hp-live-editor]");if(!o)return;if("plan"===e._mode)jo(i,s,".hp-editor-only-layer:not(.hp-plan-snap-layer)"),e._opDrag&&Wo(i,s,".wallbodies"),e._resize?.dragging&&(Yo(i,s,".wallbodies"),jo(i,s,".resize-layer"),Jo(i,s,e._resize.plan?.movingOpeningIds||[])),e._opDrag&&jo(i,s,".openinglayer");else if("decor"===e._mode){const t=e._dtDrag?.id||e._decorMove?.id;t&&Vo(i,s,t),jo(i,s,".dtframe, .backdropframe")}g(Uo(e),o);const a=s.querySelector("[data-hp-live-editor-html]");if(a){const t=e._viewOr(e._baseVb());g(e._renderLiveEditorMeasurements(t),a)}e._liveEditorPaintCount=(e._liveEditorPaintCount||0)+1}(t)})}(i),!0)}const Wo=(t,e,s)=>{for(const i of e.querySelectorAll(s))i.closest("[data-hp-live-editor]")||"hidden"!==i.style.visibility&&(i.style.visibility="hidden",t.hidden.push(i))},jo=(t,e,s)=>{for(const i of e.querySelectorAll(s))i.closest("[data-hp-live-editor]")||"0"!==i.style.opacity&&(i.style.opacity="0",t.transparent.push(i))},Go=t=>{for(const e of t.hidden)e.style.removeProperty("visibility");t.hidden.length=0;for(const e of t.transparent)e.style.removeProperty("opacity");t.transparent.length=0;for(const e of t.dimmed)e.style.removeProperty("opacity");t.dimmed.length=0},qo=(t,e,s=[])=>{if("iso"===t._renderProjection)return f``;const i=t._spaceModel();if(!i)return f``;const o=new Set(s),a=(i.rooms||[]).filter(t=>!!t.id&&o.has(t.id)).flatMap(t=>{const e=t.poly||[];return e.map((t,s)=>[t,e[(s+1)%e.length]])}),n=(t,e)=>{const[s,i]=e,o=i[0]-s[0],a=i[1]-s[1],n=o*o+a*a;if(!(n>0))return!1;const r=((t[0]-s[0])*o+(t[1]-s[1])*a)/n,l=Math.max(0,Math.min(1,r));return Math.hypot(t[0]-s[0]-o*l,t[1]-s[1]-a*l)<1e-4},r=[...t._spaceWalls.filter(t=>{const e=(t.a||[]).map(t=>t*b),s=(t.b||[]).map(t=>t*b);return a.some(t=>n(e,t)&&n(s,t))}).map((e,s)=>((e,s,i,o)=>{const a=Math.hypot(s[0]-e[0],s[1]-e[1]);if(!(a>1e-9&&i>0))return null;const n=x(i,t._cellCm,t._gridPitch)/2,r=-(s[1]-e[1])/a*n,l=(s[0]-e[0])/a*n;return{key:o,kind:"outer",cm:i,a:e,b:s,depthUnits:2*n,quad:[[e[0]+r,e[1]+l],[s[0]+r,s[1]+l],[s[0]-r,s[1]-l],[e[0]-r,e[1]-l]]}})((e.a||[]).map(t=>t*b),(e.b||[]).map(t=>t*b),e.cm,e.key||`wall-${s}`))].filter(t=>!!t),l=t._openingsR.map(t=>({x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),h=r.map(t=>v(t,l)).filter(Boolean);if(!h.length)return f``;const c=Math.max(...r.map(t=>t.depthUnits),0),d=t._viewOr(t._baseVb()),p=t._stageEl?.clientWidth&&d.w?t._stageEl.clientWidth/d.w:1,u=y(c,p)||$(w(t._cellCm),p),_=e?.color||"#607d8b",m=t._fillColors.wall_fill,g=h.join(" ");return f` + + + `},Uo=t=>{if("plan"===t._mode)return(t=>{const e=t._baseVb(),s=t._viewOr(e),i=t._opMeasureView;if(t._resize?.dragging){const e=t._spaceDisplayForRender(),i=t._resize.plan;return f``}const o=t._spaceDisplayForRender();return f``})(t);if("decor"===t._mode){const e=t._viewOr(t._baseVb()),s=t._dtDrag?.id||t._decorMove?.id||null;return f``}return d},Ko=(t,e)=>{const s=t._deviceDrag;if(!s)return;const i=t._devices.find(t=>t.id===s.id),o=[...e.querySelectorAll('[data-hp="device"]')].find(t=>t.dataset.id===s.id);if(!i||!o)return;const a=t._livePos(i),n=t._scenePoint([a.x,a.y]),r=t._viewOr(t._baseVb());o.style.left=(n[0]-r.x)/r.w*100+"%",o.style.top=(n[1]-r.y)/r.h*100+"%"},Vo=(t,e,s)=>{for(const i of e.querySelectorAll('.decorlayer [data-hp="decor"]'))i.closest("[data-hp-live-editor]")||i.dataset.id!==s||"0"!==i.style.opacity&&(i.style.opacity="0",t.transparent.push(i))},Jo=(t,e,s)=>{const i=new Set(s);for(const s of e.querySelectorAll('.openinglayer [data-hp="opening"]'))!s.closest("[data-hp-live-editor]")&&i.has(s.dataset.id||"")&&(s.style.opacity="0",t.transparent.push(s))},Yo=(t,e,s)=>{for(const i of e.querySelectorAll(s))i.closest("[data-hp-live-editor]")||(i.style.opacity="0.35",t.dimmed.push(i))};const Xo=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Zo(t){return Number.isFinite(t)?(t%360+360)%360:0}function Qo(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function ta(t){const e=Qo(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function ea(t){const e=Xo(t.r,0,255)/255,s=Xo(t.g,0,255)/255,i=Xo(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Zo(n),s:0===o?0:a/o*100,v:100*o}}function sa(t){return function(t){const e=t=>Math.round(Xo(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Zo(t.h),s=Xo(t.s,0,100)/100,i=Xo(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}const ia={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class oa extends M{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=ia,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new io(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Qo(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=oa.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=oo(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=ao(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||ia;return{title:t.title||ia.title,hue:t.hue||ia.hue,saturation:t.saturation||ia.saturation,value:t.value||ia.value,hex:t.hex||ia.hex,invalidHex:t.invalidHex||ia.invalidHex}}_syncFromColor(t,e){const s=Qo(D(t,"#607d8b"))||"#607d8b",i=ta(s);if(!i)return;const o=ea(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(sa({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Qo(e),i=s?ta(s):null;if(!s||!i)return;const o=ea(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Qo(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Qo(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=sa({h:this._hue,s:this._saturation,v:this._value}),i=sa({h:this._hue,s:100,v:100}),o=this._labels(),a=Math.round(this._hue),n=Math.round(this._saturation),r=Math.round(this._value);return p` + `}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=D(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return p` + ${this.label?p`${this.label}`:d} + + ${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):d} + `}}oa.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},oa.styles=S` + :host { + position: relative; + display: inline-flex; + align-items: center; + gap: 6px; + min-width: 0; + } + .label { + font-size: 12px; + color: var(--secondary-text-color, #9aa4ad); + white-space: nowrap; + } + .trigger { + width: 40px; + height: 40px; + flex: none; + box-sizing: border-box; + padding: 3px; + border: 1px solid var(--divider-color, #666); + border-radius: 7px; + background: + linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, + linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; + cursor: pointer; + } + :host([data-pointer-hover]) .trigger:hover { + border-color: var(--primary-color, #03a9f4); + } + .trigger[aria-expanded='true'] { + border-color: var(--primary-color, #03a9f4); + outline: none; + box-shadow: 0 0 0 1px var(--primary-color, #03a9f4); + } + .trigger:focus-visible { + border-color: var(--primary-color, #03a9f4); + outline: 2px solid var(--primary-color, #03a9f4); + outline-offset: 2px; + } + .swatch { + display: block; + width: 100%; + height: 100%; + border-radius: 3px; + box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.18); + pointer-events: none; + } + .picker { + /* The popover attribute promotes this surface into the browser top layer, outside + hp-dialog's scrolling/clipping body. JS supplies viewport coordinates. */ + position: fixed; + z-index: 2147483647; + inset: auto; + top: 0; + left: 0; + width: min(292px, calc(100vw - 16px)); + max-height: calc(100vh - 16px); + margin: 0; + box-sizing: border-box; + display: grid; + gap: 10px; + padding: 12px; + overflow: auto; + color: var(--primary-text-color, #fff); + background: var(--card-background-color, #202126); + border: 1px solid var(--primary-color, #03a9f4); + border-radius: 10px; + box-shadow: 0 10px 28px rgb(0 0 0 / 0.34); + } + .picker[popover]:not(:popover-open) { + /* Keep the author-level display:grid from exposing the surface for one + frame before showPopover() promotes it into the top layer. */ + display: none; + } + .picker-head, + .row, + .control-head { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + } + .picker-head { + justify-content: space-between; + } + .caption { + flex: 1; + min-width: 0; + overflow: hidden; + color: var(--secondary-text-color, #9aa4ad); + font-size: 12px; + text-overflow: ellipsis; + white-space: nowrap; + } + .preview { + width: 52px; + height: 30px; + flex: none; + box-sizing: border-box; + padding: 3px; + border: 1px solid var(--divider-color, #666); + border-radius: 7px; + background: + linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, + linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, + linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; + } + .preview > span { + display: block; + width: 100%; + height: 100%; + border-radius: 3px; + } + .sv-field { + position: relative; + width: 100%; + height: 132px; + overflow: hidden; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 8px; + background: + linear-gradient(to top, #000, transparent), + linear-gradient(to right, #fff, var(--hp-picker-hue, #f00)); + cursor: crosshair; + touch-action: none; + user-select: none; + } + .sv-thumb { + position: absolute; + left: var(--hp-picker-saturation, 0%); + top: var(--hp-picker-value, 100%); + width: 14px; + height: 14px; + box-sizing: border-box; + border: 2px solid #fff; + border-radius: 50%; + box-shadow: 0 0 0 1px rgb(0 0 0 / .65), 0 1px 3px rgb(0 0 0 / .5); + transform: translate(-50%, -50%); + pointer-events: none; + } + .control { + display: grid; + gap: 2px; + min-width: 0; + } + .control-value { + flex: none; + color: var(--secondary-text-color, #9aa4ad); + font-size: 12px; + } + input[type='range'] { + width: auto; + height: 40px; + min-width: 0; + flex: 1; + margin: -5px 0; + accent-color: var(--primary-color, #03a9f4); + touch-action: none; + } + .hue-range { + accent-color: var(--hp-picker-hue, #f00); + --hp-picker-hue-track: linear-gradient(to right, + #f00 0%, #ff0 16.667%, #0f0 33.333%, #0ff 50%, + #00f 66.667%, #f0f 83.333%, #f00 100%); + --hp-picker-hue-thumb-shadow: + 0 0 0 2px var(--card-background-color, #202126), + 0 0 0 3px var(--primary-text-color, #fff); + } + .hue-range::-webkit-slider-runnable-track { + height: 10px; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 999px; + background: var(--hp-picker-hue-track); + } + .hue-range::-moz-range-track { + height: 10px; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 999px; + background: var(--hp-picker-hue-track); + } + .hue-range::-moz-range-progress { + height: 10px; + border: 0; + border-radius: 999px; + background: transparent; + } + .hue-range::-webkit-slider-thumb { + border-radius: 50%; + box-shadow: var(--hp-picker-hue-thumb-shadow); + } + .hue-range::-moz-range-thumb { + border-radius: 50%; + box-shadow: var(--hp-picker-hue-thumb-shadow); + } + input[type='text'], + input[type='number'] { + min-height: 40px; + box-sizing: border-box; + border: 1px solid var(--divider-color, #666); + border-radius: 6px; + padding: 6px 8px; + color: var(--primary-text-color, #fff); + background: var(--input-fill-color, transparent); + font: inherit; + } + input[type='text'] { + width: 100%; + } + input[type='number'] { + width: 50px; + flex: none; + } + input:focus-visible, + .sv-field:focus-visible { + outline: 2px solid var(--primary-color, #03a9f4); + outline-offset: 1px; + } + input[aria-invalid='true'] { + border-color: var(--error-color, #db4437); + } + .error { + color: var(--error-color, #db4437); + font-size: 11px; + line-height: 1.25; + } + .pct { + color: var(--secondary-text-color, #9aa4ad); + font-size: 12px; + } + :host([disabled]) { + opacity: .5; + pointer-events: none; + } + @media (prefers-reduced-motion: reduce) { + .picker { transition: none !important; } + } + @media (forced-colors: active) { + .hue-range::-webkit-slider-runnable-track { + border-color: ButtonText; + background: Canvas; + } + .hue-range::-moz-range-track { + border-color: ButtonText; + background: Canvas; + } + .hue-range::-webkit-slider-thumb { + box-shadow: none; + } + .hue-range::-moz-range-thumb { + box-shadow: none; + } + } + `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",oa);const aa=new WeakMap;function na(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=aa.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},aa.set(e,s)),s}function ra(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function la(t,e){const s=na(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,ra(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,ra(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,ra(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function ha(t){const e=na(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function ca(t,e,s=ha(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function da(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:ca(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function pa(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function ua(t,e,s,i=ha(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return da(t,o.platform||n,"entity-platform",i,a)}class _a extends M{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=la(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return l(this.hass)}_t(t,e){return P(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=ha(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=ua(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return da(t,s?.domain,"registry-owner",i,e)});if(l.some(Boolean))return pa(l);const h=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&h.push(ua(t,e,s,i));return h.some(Boolean)?pa(h):pa((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return da(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,ha(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=ua(this.hass,t,this.registry,ha(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return d;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=I(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),o="none"!==t.pulse.kind,a=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let n=-a/2,r=a/2,l=-a/2,h=a/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(r=Math.max(r,i+t)),"left"===s.valueBadge.position&&(n=Math.min(n,-i-t)),"top"===s.valueBadge.position&&(l=Math.min(l,-i-e)),"bottom"===s.valueBadge.position&&(h=Math.max(h,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);h=Math.max(h,t)}const c=Math.min(1,2.35/Math.max(1,r-n),2.35/Math.max(1,h-l)),u=Math.round(100*c),_=[`left:calc(50% - ${54*((n+r)/2)*c}px)`,`top:calc(50% - ${54*((l+h)/2)*c}px)`,c<1?`transform:scale(${c})`:""].filter(Boolean).join(";"),m=["dev",T(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),g=z(s).join(";"),f=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),b=[this.deviceName,this._providerSummary(i),this._stateSummary(t),f,t.valueFullText||"",R(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return p`
    +
    + ${this._t("marker.preview.title")} + + ${this._t(e?"marker.preview.example":"marker.preview.actual")} + +
    +
    + +
    +
    ${this._t("marker.preview.integration")}${this._providerSummary(i)}
    +
    ${this._t("marker.preview.source")}${this._sourceSummary(t)}
    +
    ${this._t("marker.preview.current_state")}${this._stateSummary(t)}
    +
    ${this._t("marker.preview.result")}${f}
    + ${t.fallbackReason?p`

    ${this._reason(t.fallbackReason)}

    `:d} + ${t.explanation.notices.map(t=>p`

    ${this._reason(t)}

    `)} + ${c<1?p`

    ${this._t("marker.preview.scaled",{n:u})}

    `:d} + ${e&&this._reducedMotion?p`

    ${this._t("marker.preview.reduced_motion")}

    `:d} +
    +
    + ${t.visualSources.length||t.criticalSources.length?p`
    + ${this._t("marker.preview.details")} +
    + ${[...t.visualSources,...t.criticalSources].map(t=>p` +
    + ${t.name}${t.eid} + ${t.stateText||t.state} + ${this._sourceProvider(t.eid)} +
    `)} +
    +
    `:d} + ${"icon_ripple"===t.display?p`
    + + +
    `:d} +
    `}}_a.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},_a.styles=[C,S` + :host { display: block; min-width: 0; } + .devicepreview { + margin: 4px 0 14px; + padding: 14px; + border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.28)); + border-radius: 14px; + background: color-mix(in srgb, var(--card-background-color, #20232b) 92%, var(--primary-color, #03a9f4)); + color: var(--primary-text-color, #fff); + overflow: hidden; + } + .previewhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; } + .previewbadge { + padding: 3px 9px; + border-radius: 999px; + color: var(--secondary-text-color, #9aa0aa); + background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 12%, transparent); + font-size: 12px; + font-weight: 700; + } + .previewbadge.example { color: var(--warning-color, #ffb300); } + .previewgrid { display: grid; grid-template-columns: minmax(170px, 0.8fr) minmax(240px, 1.2fr); gap: 16px; align-items: stretch; } + .previewstage { + position: relative; + min-height: 168px; + border-radius: 12px; + background: var(--secondary-background-color, rgba(0, 0, 0, 0.16)); + overflow: hidden; + pointer-events: none; + container-type: inline-size; + --icon-size: 54px; + --device-base-size: 48.6px; + } + .previewfit { position: absolute; left: 50%; top: 50%; transform-origin: center; } + .previewstage .dev { left: 0; top: 0; cursor: default; } + :host([data-pointer-hover]) .previewstage .dev:hover { z-index: 2; } + .previewfacts { min-width: 0; display: grid; align-content: start; gap: 8px; } + .previewfacts > div { display: grid; grid-template-columns: minmax(115px, 0.8fr) minmax(0, 1.3fr); gap: 8px; } + .previewfacts span { color: var(--secondary-text-color, #9aa0aa); } + .previewfacts b { min-width: 0; overflow-wrap: anywhere; } + .previewnotice { margin: 2px 0 0; color: var(--secondary-text-color, #9aa0aa); font-size: 13px; } + .previewdetails { margin-top: 12px; } + .previewdetails summary { cursor: pointer; color: var(--secondary-text-color, #9aa0aa); } + .previewtable { display: grid; gap: 8px; margin-top: 9px; } + .previewsource { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3px 10px; font-size: 13px; } + .previewsource > * { min-width: 0; overflow-wrap: anywhere; } + .previewsource code { color: var(--secondary-text-color, #9aa0aa); } + .previewdemo { + min-height: 40px; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.35)); + border-radius: 10px; + background: transparent; + color: inherit; + cursor: pointer; + } + .previewdemos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } + .previewdemo:focus-visible { outline: 2px solid var(--primary-color, #03a9f4); outline-offset: 2px; } + .previewdemo:disabled { opacity: 0.5; cursor: not-allowed; } + @media (max-width: 640px) { + .previewgrid { grid-template-columns: minmax(0, 1fr); } + .previewstage { min-height: 142px; } + .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } + } + `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",_a);const ma={revision:0,topologies:[],states:{}};class ga extends M{constructor(){super(...arguments),this.value={enabled:!1,z2mBaseTopics:[]},this.savedEnabled=!1,this.devices=[],this._runtime=null,this._snapshot=ma,this._topicText="zigbee2mqtt",this._invalidTopic=!1}disconnectedCallback(){this._release?.(),this._release=void 0,super.disconnectedCallback()}updated(t){t.has("hass")&&(this._release?.(),this._release=void 0,this._runtime=null,this._snapshot=ma),this.savedEnabled&&this._admin&&this._ensureRuntime()}willUpdate(t){t.has("value")&&(this._topicText=(this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]).join("\n"),this._invalidTopic=!1)}get _admin(){return!0===this.hass?.user?.is_admin}_t(t,e){return go(l(this.hass),t,e)}async _ensureRuntime(){return this._runtime||(this._runtime=await import("./zigbee-topology-runtime-DUxfg4Z9.js"),this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this._release=this._runtime.subscribeZigbeeTopology(this.hass,()=>{this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this.requestUpdate()}),this.requestUpdate()),this._runtime}_emit(t){this._admin&&this.dispatchEvent(new CustomEvent("hp-topology-settings-change",{detail:t,bubbles:!0,composed:!0}))}_topics(){return this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]}_editTopics(t){this._topicText=t;const e=t.split(/[\r\n,]+/).map(t=>t.trim()).filter(Boolean),s=e.map(F).filter(Boolean);this._invalidTopic=s.length!==e.length,this._invalidTopic?this.requestUpdate():this._emit({...this.value,z2mBaseTopics:[...new Set(s)].slice(0,8)})}_status(t){const e=this._snapshot.states[t];if(!e)return this._t("status_idle");if("loading"===e.phase)return this._t("status_loading");if("error"===e.phase)return this._t(`error_${e.error||"provider"}`);if("ready"!==e.phase||!e.obtainedAt)return this._t("status_idle");const s=new Date(e.obtainedAt).toLocaleTimeString(l(this.hass),{hour:"2-digit",minute:"2-digit"}),i=this._snapshot.topologies.find(e=>"zha"===t?"zha"===e.provider:`z2m:${e.instanceId}`===t),o=!!i&&!!this.registry&&fo(i,this.devices,this.registry).warnings.length>0;return e.partial||o?this._t("status_partial",{time:s}):i&&!i.links.length?this._t("status_no_links",{time:s}):this._t(Date.now()-e.obtainedAt>bo?"status_stale":"status_ready",{time:s})}_busy(t){return"loading"===this._snapshot.states[t]?.phase}async _readZha(){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).readZhaTopology(this.hass)}async _refreshZ2m(t){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).refreshZ2mTopology(this.hass,t)}render(){const t=this._admin,e=this.value.enabled,s=t&&this.savedEnabled,i=this._topics();return p` +
    ${this._t("title")}
    + +
    ${this._t("hint")}
    + ${t?d:p`
    ${this._t("admin_only")}
    `} + ${e?p`
    + ${this.savedEnabled?d:p`
    ${this._t("save_first")}
    `} +
    +
    ${this._t("zha")}
    +
    ${this._t("zha_hint")}
    +
    + + ${this._status("zha")} +
    +
    +
    +
    ${this._t("z2m")}
    + + + ${this._invalidTopic?p`
    ${this._t("error_invalid_topic")}
    `:d} +
    ${this._t("z2m_warning")}
    + ${i.map(t=>p`
    + + ${this._status(`z2m:${t}`)} +
    `)} +
    +
    `:d} + `}}ga.properties={hass:{attribute:!1},value:{attribute:!1},savedEnabled:{type:Boolean,attribute:"saved-enabled"},devices:{attribute:!1},registry:{attribute:!1}},ga.styles=S` + :host { display: block; color: inherit; font: inherit; } + .section { margin-top: 18px; font-weight: 700; } + .toggle { display: flex; align-items: center; gap: 10px; margin-top: 10px; } + .hint, .status { color: var(--secondary-text-color, #9aa0aa); font-size: 12px; line-height: 1.45; } + .hint { margin-top: 6px; } + .providers { display: grid; gap: 14px; margin-top: 12px; padding-left: 34px; } + .provider { display: grid; gap: 7px; } + .provider-title { font-weight: 650; } + .actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } + textarea { + box-sizing: border-box; width: 100%; min-height: 58px; resize: vertical; + border: 1px solid var(--divider-color, #666); border-radius: 8px; padding: 8px; + color: var(--primary-text-color, inherit); background: var(--card-background-color, #202126); + font: inherit; + } + button { + display: inline-flex; align-items: center; gap: 6px; min-height: 36px; + border: 1px solid var(--divider-color, #666); border-radius: 9px; padding: 7px 11px; + color: inherit; background: transparent; cursor: pointer; font: inherit; font-weight: 600; + } + button:disabled, textarea:disabled { opacity: .5; cursor: default; } + .warning { color: var(--warning-color, #d89300); font-size: 12px; line-height: 1.45; } + `,customElements.get("hp-zigbee-topology-settings")||customElements.define("hp-zigbee-topology-settings",ga);const fa=(t,e)=>[t[0]-e[0],t[1]-e[1]],ba=(t,e)=>[t[0]+e[0],t[1]+e[1]],va=(t,e)=>t[0]*e[0]+t[1]*e[1],ya=t=>Math.hypot(t[0],t[1]);function $a(t){let e=0;for(let s=0;st[1]!=l>t[1]&&t[0]<(r-a)*(t[1]-n)/(l-n)+a&&(s=!s)}return s}([r[0]+n[0]*l,r[1]+n[1]*l],t)&&(n=[-n[0],-n[1]]),n}function xa(t){const e=t.length;if(e<3)return!1;for(let s=0;s(t[0]-o[0])*r[1]-(t[1]-o[1])*r[0],h=t=>(t[0]-o[0])*r[0]+(t[1]-o[1])*r[1],c=s,d=n-s;for(const e of t){const t=Math.abs(l(e));if(t<=s)continue;const o=h(e);o<=c||o>=d||tMath.abs(n+(t-p)/g*(r-n));i=Math.min(i,f(_),f(m))}}return i}function Ma(t,e,s){if(E(t,e,s))return!0;if(L(t,e,s)||L(e,t,s))return!1;let i=0;try{const s=O([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of s)t?.[0]&&(i+=B(t[0]))}catch{return!1}return i>Math.max(1e-7,s*s)}function Da(t,e,s){for(const i of e)for(let e=0;es)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-s&&h+t.length/2<=r+s)return!0}return!1}function Ca(t,e,s){const i=Math.abs(e[0]-t[0]),o=Math.abs(e[1]-t[1]);return i<=s&&o>s?"v":o<=s&&i>s?"h":null}function Pa(t,e,s){return Math.abs(t[0]-e[0])<=s&&Math.abs(t[1]-e[1])<=s}function Ia(t,e,s,i,o){return Pa(t,s,o)&&Pa(e,i,o)||Pa(t,i,o)&&Pa(e,s,o)}function Ta(t,e,s,i,o){const a=Ca(t,e,o);return a&&Ca(s,i,o)===a?"h"===a?Math.abs(t[1]-s[1])>o||Math.abs(t[1]-i[1])>o?0:Math.max(0,Math.min(Math.max(t[0],e[0]),Math.max(s[0],i[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s[0],i[0]))):Math.abs(t[0]-s[0])>o||Math.abs(t[0]-i[0])>o?0:Math.max(0,Math.min(Math.max(t[1],e[1]),Math.max(s[1],i[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s[1],i[1]))):0}function za(t,e,s,i){const o=t.length,a=Ca(t[(e-1+o)%o],t[e],i),n=Ca(t[(e+1)%o],t[(e+2)%o],i),r="h"===s?"v":"h";return a===r&&n===r}function Ra(t){let e=1/0,s=1/0,i=-1/0,o=-1/0;for(const a of t)e=Math.min(e,a[0]),s=Math.min(s,a[1]),i=Math.max(i,a[0]),o=Math.max(o,a[1]);return[e,s,i,o]}function Aa(t,e,s){const i=Ra(t),o=Ra(e);return i[2]o}const Na=(t,e)=>"h"===e?t[0]:t[1],Ha=(t,e)=>"h"===e?t[1]:t[0];function La(t,e,s,i,o){const a=t.find(t=>t.id===e);if(!a)return null;const n=a.poly[s],r=a.poly[(s+1)%a.poly.length],l=Ca(n,r,o);if(!l)return null;const h=0===i?r:n,c=0===i?n:r,d=Na(h,l),p=Na(c,l),u=Ha(h,l),_=function(t,e,s,i){const o=[];for(const a of t)for(let t=0;ti||Math.abs(Ha(r,e)-s)>i)continue;const l=Na(n,e),h=Na(r,e),c=Math.min(l,h),d=Math.max(l,h);d-c>i&&o.push({roomId:a.id,edge:t,lo:c,hi:d})}return o.sort((t,e)=>t.lo-e.lo||t.hi-e.hi||t.roomId.localeCompare(e.roomId)||t.edge-e.edge)}(t,l,u,o),m=function(t,e,s,i){const o=[e,s];for(const a of t){const t=Math.max(e,a.lo),n=Math.min(s,a.hi);n-t>i&&o.push(t,n)}const a=[...new Set(o)].sort((t,e)=>t-e),n=[];for(let e=0;e+1r>t.lo-i&&rt.roomId))].sort();n.push({lo:s,hi:o,owners:l})}return n}(_,Math.min(d,p),Math.max(d,p),o);return!m.length||m.some(t=>t.owners.length<1||t.owners.length>2)?null:{roomId:e,edge:s,movedEndpoint:i,axis:l,line:u,fixed:d,moving:p,intervals:_,baseline:m}}function Ea(t,e,s){const i=[];for(const o of t.intervals){const a=e.polys[o.roomId];if(!a){i.push(o);continue}if(o.edge<0||o.edge>=a.length)continue;const n=a[o.edge],r=a[(o.edge+1)%a.length];if(Ca(n,r,s)!==t.axis||Math.abs(Ha(n,t.axis)-t.line)>s||Math.abs(Ha(r,t.axis)-t.line)>s)continue;const l=Na(n,t.axis),h=Na(r,t.axis),c=Math.min(l,h),d=Math.max(l,h);d-c>s&&i.push({...o,lo:c,hi:d})}return i}function Oa(t,e,s){return new Set(t.filter(t=>e>t.lo-s&&et.roomId)).size}function Ba(t,e,s){for(const i of e.sideOwnership){const e=t.polys[i.roomId];if(!e)return!1;const o=e[i.edge],a=e[(i.edge+1)%e.length];if(Ca(o,a,s)!==i.axis)return!1;const n=Na(0===i.movedEndpoint?o:a,i.axis),r=Ea(i,t,s),l=(i.moving>=i.fixed?i.baseline[i.baseline.length-1]:i.baseline[0]).owners.length;if(l<1||l>2)return!1;const h=Math.min(i.fixed,i.moving,n),c=Math.max(i.fixed,i.moving,n),d=[h,c,i.fixed,i.moving,n];for(const t of i.intervals)d.push(t.lo,t.hi);for(const t of r)d.push(t.lo,t.hi);const p=[...new Set(d.filter(t=>t>=h&&t<=c))].sort((t,e)=>t-e),u=Math.min(i.fixed,i.moving),_=Math.max(i.fixed,i.moving),m=Math.min(i.fixed,n),g=Math.max(i.fixed,n);for(let t=0;t+1u-s&&a<_+s,h=a>m-s&&a2||d>2)return!1;if(n&&h){if(c!==d)return!1}else if(!n&&h){if(d!==l||c>0&&c!==d)return!1}else if(2===c?2!==d:d>1)return!1}}return!0}function Wa(t,e,s,i){const o=[s.n[0]*i,s.n[1]*i],a={polys:{},openings:{},movedSpans:{}};for(const e of s.roomIds){const i=t.find(t=>t.id===e),n=s.edgeByRoom[e];if(!i||null==n)continue;const r=(n+1)%i.poly.length;a.polys[e]=i.poly.map((t,e)=>e===n||e===r?ba(t,o):[...t]),a.movedSpans[e]=[[ba(i.poly[n],o),ba(i.poly[r],o)]]}for(const t of e)s.movingOpeningIds.includes(t.id)&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function ja(t,e,s,i,o,a,n,r){if(!function(t,e,s,i){if(wa([t.x,t.y],e,s)>2*i)return!1;const o=ya(fa(s,e));if(o<=i)return!1;const a=[(s[0]-e[0])/o,(s[1]-e[1])/o],n=(t.x-e[0])*a[0]+(t.y-e[1])*a[1];return n>=-i&&n<=o+i}(t,e,s,r))return!0;if(wa([t.x,t.y],i,o)>2*r)return!1;const l=0===a?i:o;return ya(fa([t.x,t.y],l))>=t.length/2+n-r}function Ga(t,e,s,i){const o=i.movingHalf||0;return"circle"===t.kind?wa(t.center,e,s)2)return!1;const r=Wa(t,e,s,i);if(Object.keys(r.polys).length!==s.roomIds.length)return!1;if(!Ba(r,s,a))return!1;const l=new Set(s.roomIds),h=t=>r.polys[t.id]||t.poly;for(const i of s.roomIds){const l=t.find(t=>t.id===i),h=r.polys[i],c=s.edgeByRoom[i];if(!l||!h||h.length!==s.topology[i]||h.length!==l.poly.length||!xa(h))return!1;const d=l.poly.length,p=(c-1+d)%d,u=[p,c,(c+1)%d];for(const t of u)if(H(h[t],h[(t+1)%d]))return!1;const _=$a(l.poly),m=$a(h);if(Math.abs(m)t.id===e),i=r.polys[e];for(const o of t){if(o.id===e||l.has(o.id))continue;const t=h(o);if(!Aa(s.poly,o.poly,a)||!Aa(i,t,a))if(L(s.poly,o.poly,a)){if(!L(i,t,a))return!1}else if(L(o.poly,s.poly,a)){if(!L(t,i,a))return!1}else if(L(i,t,a)||L(t,i,a)||Ma(i,t,a))return!1}}t.find(t=>t.id===s.roomId);const c=r.polys[s.roomId],d=s.edgeByRoom[s.roomId],p=c[d],u=c[(d+1)%c.length];if(Math.abs(i)>a)for(const t of o.obstacles||[])if(Ga(t,p,u,o))return!1;for(const t of e){if(!s.movingOpeningIds.includes(t.id))continue;const e=r.openings[t.id];if(!e||t.hosted)return!1;if(!Da({...t,x:e[0],y:e[1]},[c],2*a))return!1}return!0}const Ua=new WeakMap,Ka=t=>"number"==typeof t&&Number.isFinite(t),Va=(t,e)=>{const s=new Map;for(const i of t||[])!Ka(i?.cm)||!e&&i.cm<=0||s.set(i.cm,(s.get(i.cm)||0)+1);return s},Ja=t=>[String(t),"см"].join(" ");function Ya(t,e,s={}){if(s.allowClear)return[];const i=!0===s.exactMultiplicity,o=Va(t,i),a=Va(e,i),n=[];for(const[t,e]of o){const s=a.get(t)||0;0===s?n.push({invariant:"wall_records",kind:"lost",owner:Ja(t),reference:`было ${e}`,detail:"записи этой толщины исчезли целиком"}):i&&s!==e&&n.push({invariant:"wall_records",kind:"count",owner:Ja(t),reference:`было ${e}, стало ${s}`,detail:"изменилось число записей этой толщины"})}if(i)for(const[t,e]of a)o.has(t)||n.push({invariant:"wall_records",kind:"count",owner:Ja(t),reference:`было 0, стало ${e}`,detail:"появились записи новой толщины"});return n}class Xa{constructor(){this._selectedRoomId=null,this._session=null,this._eligibility=null}get dragging(){return null!==this._session}ownsPointer(t){return this._session?.pointerId===t}get selectedRoomId(){return this._selectedRoomId}get snapshotIdentity(){return this._session?.snapshotIdentity||null}get activePointerId(){return this._session?.pointerId??null}get moved(){return!0===this._session?.moved}get delta(){return this._session?.delta||0}get rooms(){return this._session?.rooms||null}get openings(){return this._session?.openings||null}get plan(){return this._session?.plan||null}get preview(){return this._session?.accepted?.preview||null}get liveLabels(){return this._session?.accepted?.labels||null}selectRoom(t){this._selectedRoomId=t}restoreSelection(t){this._selectedRoomId=t}escapeIdle(){return this._selectedRoomId?(this._selectedRoomId=null,"selection-cleared"):"exit-tool"}resolve(t,e,s){this._eligibility&&this._eligibility.context===t||(this._eligibility={context:t,values:new Map});const i=this._eligibility.values.get(e);if(i)return i;const o=s();return this._eligibility.values.set(e,o),o}begin(t){return!this._session&&(this._session={pointerId:t.pointerId,start:[...t.start],roomId:t.roomId,plan:t.plan,options:t.options,rooms:t.rooms,openings:t.openings,snapshotIdentity:t.snapshotIdentity,before:t.before,moved:!1,delta:0,changedRoomIds:[...t.plan.roomIds],rejectionNotified:!1,wallUnionBefore:t.wallUnionBefore,epochBefore:t.epochBefore,accepted:null},!0)}move(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=(i=e.start,o=t.point,a=e.plan.n,[i?.[0],i?.[1],o?.[0],o?.[1],a?.[0],a?.[1]].every(Number.isFinite)?(o[0]-i[0])*a[0]+(o[1]-i[1])*a[1]:0);var i,o,a;const n=t.snap([e.plan.a[0]+e.plan.n[0]*s,e.plan.a[1]+e.plan.n[1]*s]),r=(n[0]-e.plan.a[0])*e.plan.n[0]+(n[1]-e.plan.a[1])*e.plan.n[1],l=function(t,e,s,i,o,a){if(!Number.isFinite(i)||Math.abs(i)<1e-9)return 0;const n=Math.sign(i),r=Math.abs(i),l=Math.max(Math.abs(o),1e-6);let h=Ua.get(s);h&&h.opts===a||(h={opts:a,values:new Map},Ua.set(s,h));const c=h.values;let d=0;for(let i=Math.min(l,r),o=0;o<4096&&i<=r+1e-9;o++,i=Math.min(r,i+l)){const o=n*i,h=`${o.toFixed(9)}|${l.toFixed(9)}`;let p=c.get(h);if(void 0===p&&(p=qa(t,e,s,o,a),c.size<4096&&c.set(h,p)),!p)break;if(d=o,Math.abs(i-r)<=1e-9)break}return d}(e.rooms,e.openings,e.plan,r,t.step,e.options);if(e.moved&&l===e.delta)return{kind:"no-op"};const h=Wa(e.rooms,e.openings,e.plan,l);let c;try{if(c=t.project(e.snapshotIdentity,h.polys,h.openings,e.changedRoomIds,e.rooms),!c.ok)return this._projectionRejected(e);if(Ya(c.value.beforeWalls,c.value.afterWalls,{exactMultiplicity:!0}).length)return this._projectionRejected(e)}catch{return this._projectionRejected(e)}const d=e.accepted,p={preview:c.value.preview,labels:null,artifact:c.value.artifact,beforeWalls:c.value.beforeWalls,afterWalls:c.value.afterWalls};e.accepted=p;try{t.publish(p.preview,p.artifact),p.labels=t.measure(h,e.plan)}catch{e.accepted=d;try{t.publish(d?.preview||null,d?.artifact||null)}catch{}return this._projectionRejected(e)}return e.moved=!0,e.delta=l,{kind:"accepted",preview:c.value.preview,labels:p.labels,artifact:c.value.artifact}}finish(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=e.accepted;let i;if(e.moved&&Math.abs(e.delta)>1e-9&&s)if(t.currentSnapshotIdentity!==e.snapshotIdentity)i={kind:"rejected",reason:"stale-snapshot"};else if(qa(e.rooms,e.openings,e.plan,e.delta,e.options))if(Ya(s.beforeWalls,s.afterWalls,{exactMultiplicity:!0}).length)i={kind:"rejected",reason:"wall-records"};else{let o=!1;try{o=t.validatePreview(s.preview)}catch{o=!1}i=o?{kind:"commit",preview:s.preview,before:e.before}:{kind:"rejected",reason:"invalid-candidate"}}else i={kind:"rejected",reason:"invalid-topology"};else i={kind:"no-op"};return this._session=null,"commit"===i.kind&&(this._eligibility=null),i}cancel(t,e){const s=this._session;if(!s||void 0!==e&&s.pointerId!==e)return{kind:"no-op"};this._session=null,this._eligibility=null;const i=t===s.snapshotIdentity;return{kind:"cancelled",restoreWallUnion:i?s.wallUnionBefore:null,restoreEpoch:i?s.epochBefore:null}}reset(){this._selectedRoomId=null,this._session=null,this._eligibility=null}_projectionRejected(t){const e=!t.rejectionNotified;return t.rejectionNotified=!0,{kind:"rejected",notify:e}}}const Za=t=>"object"==typeof t&&null!==t?t:null,Qa=t=>Array.isArray(t)?t.flatMap(t=>{const e=Za(t);return e?[e]:[]}):[],tn=t=>{if(!Array.isArray(t))return null;const e=Number(t[0]),s=Number(t[1]);return Number.isFinite(e)&&Number.isFinite(s)?[e,s]:null},en=t=>Array.isArray(t)?t.flatMap(t=>{const e=tn(t);return e?[e]:[]}):[];function sn(t){const e=en(t.poly);if(e.length>=3)return e;const s=Number(t.x),i=Number(t.y),o=Number(t.w),a=Number(t.h);return[s,i,o,a].every(Number.isFinite)?[[s,i],[s+o,i],[s+o,i+a],[s,i+a]]:[]}function on(t){if(!t.length)return null;const e=t.map(t=>t[0]),s=t.map(t=>t[1]);return{minX:Math.min(...e),minY:Math.min(...s),maxX:Math.max(...e),maxY:Math.max(...s)}}function an(t,e){const s=Za(t);if(!s)return null;const i=Qa(s.rooms),o=new Set(function(t,e){const s=Qa(t),i=new Set(e);return s.flatMap(t=>{const e=String(t.id||"");return e&&i.has(e)?[e]:[]})}(i,e)),a=i.filter(t=>o.has(String(t.id||"")));if(!on(a.flatMap(sn))||!a.length)return null;const n=Number(s.cell_cm),r=Number.isFinite(n)&&n>0?n:5,l=a.flatMap(t=>{const e=sn(t);return e.map((t,s)=>[t,e[(s+1)%e.length]])}),h=.02*W,c=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(t[0]-e[0]-n*i,t[1]-e[1]-n*o)},d=(t,e)=>Math.min(...l.map(([s,i])=>((t,e,s,i)=>{const o=(t,e,s)=>(e[0]-t[0])*(s[1]-t[1])-(e[1]-t[1])*(s[0]-t[0]),a=o(t,e,s),n=o(t,e,i),r=o(s,i,t),l=o(s,i,e);return a*n<=h*h&&r*l<=h*h&&Math.min(t[0],e[0])<=Math.max(s[0],i[0])+h&&Math.min(s[0],i[0])<=Math.max(t[0],e[0])+h&&Math.min(t[1],e[1])<=Math.max(s[1],i[1])+h&&Math.min(s[1],i[1])<=Math.max(t[1],e[1])+h?0:Math.min(c(t,s,i),c(e,s,i),c(s,t,e),c(i,t,e))})(t,e,s,i))),p=t=>{const e=tn(t.a),s=tn(t.b);if(e&&s)return((t,e)=>l.some(([s,i])=>{const o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a);if(!(n>h))return!1;const r=t=>Math.abs((t[0]-s[0])*a-(t[1]-s[1])*o)/n;if(r(t)>h||r(e)>h)return!1;const l=t=>((t[0]-s[0])*o+(t[1]-s[1])*a)/(n*n),c=l(t),d=l(e);return Math.min(1,Math.max(c,d))-Math.max(0,Math.min(c,d))>h}))(e,s);const[i]=String(t.key||"").split("@"),o=i?.split(",").map(Number)||[];return 2!==o.length||!o.every(Number.isFinite)||l.some(([t,e])=>c(o,t,e)<=h)},u=Qa(s.walls).filter(p),_=Qa(s.wall_segments).filter(p),m=Math.max(0,...[...u,..._].map(t=>Number(t.cm)).filter(Number.isFinite))/r*W/2,g=Qa(s.partitions).filter(t=>{const e=tn(t.a),s=tn(t.b);if(!e||!s)return!0;const i=Math.max(0,Number(t.cm)||0)/r*W/2;return d(e,s)<=m+i+h}),f=Qa(s.room_drafts).filter(t=>{const e=en(t.points);if(e.length<2)return!0;const s=Qa(t.segments);return e.some((t,i)=>{if(i+1>=e.length)return!1;const o=Math.max(0,Number(s[i]?.cm)||0)/r*W/2;return d(t,e[i+1])<=m+o+h})}),b=Qa(s.wall_columns).filter(t=>{const e=tn(t.center);if(!e)return!0;const s=Math.max(0,Number(t.cm)||0)/r*W;return l.some(([t,i])=>c(e,t,i)<=m+s+h)}),v=new Set(g.map(t=>String(t.id||"")).filter(Boolean)),y=Qa(s.openings).filter(t=>{const e=Za(t.host);if("partition"===e?.kind)return v.has(String(e.id||""));const s=tn([t.x,t.y]);return!s||l.some(([t,e])=>c(s,t,e)<=h)});return{...s,rooms:a,walls:u,wall_segments:_,open_spans:Qa(s.open_spans).filter(p),partitions:g,room_drafts:f,wall_columns:b,openings:y}}const nn=(t,e)=>[(t[0]-e.x)/Math.max(1e-9,e.w)*e.stageWidth,(t[1]-e.y)/Math.max(1e-9,e.h)*e.stageHeight];function rn(t){const{poly:e,edge:s,text:i,view:o}=t,a=e[s],n=e[(s+1)%e.length],r=[(a[0]+n[0])/2,(a[1]+n[1])/2],l=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return(t=>{let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(e,s),h=Math.hypot(n[0]-a[0],n[1]-a[1])||1,c=[(n[0]-a[0])/h,(n[1]-a[1])/h],d=nn(r,o),p=nn(t.gearCenter,o),u=Math.max(34,7.2*i.length+12),_=Math.abs(l[0])>=Math.abs(l[1])?Math.max(28,u/2+4):Math.max(28,13),m=d[0]+l[0]*_,g=d[1]+l[1]*_,f=e=>{return s=m+c[0]*e,i=g+c[1]*e,o=u,a=18,n=p[0],r=p[1],l=t.gearWidthPx,h=t.gearHeightPx,2*Math.abs(s-n)=Math.abs(c[1])?c[0]:c[1])>0?-1:1,e=Math.max(o.stageWidth,o.stageHeight,64);for(let s=4;s<=e;s+=4){const e=s*t;if(!f(e)){b=e;break}const i=-e;if(!f(i)){b=i;break}}}const v=l[0]*_+c[0]*b,y=l[1]*_+c[1]*b,$=Math.hypot(v,y)||1,w=y/$*12,k=[r[0]+v/$*12*o.w/Math.max(1,o.stageWidth),r[1]+w*o.h/Math.max(1,o.stageHeight)];return{anchor:r,offsetXPx:v,offsetYPx:y,tangentOffsetPx:b,side:Math.abs(l[0])>=Math.abs(l[1])?l[0]<0?"left":"right":l[1]<0?"above":"below",leader:{a:r,b:k}}}const ln=t=>{let e=(t%360+360)%360;return e>180&&(e-=360),e};function hn(t,e,s,i,o,a=0,n=[t,e],r){const l=1e-7,h=(t,e,s,i,o)=>{const a=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*i)/o));return{t:a,x:e[0]+a*s,y:e[1]+a*i}},c=t=>{if(!t||!Number.isFinite(t[0])||!Number.isFinite(t[1]))return null;const e=Math.hypot(t[0],t[1]);return e>1e-9?[t[0]/e,t[1]/e]:null},d=c(r);let p=null;for(const r of i){const i=Number(r?.a?.[0]),u=Number(r?.a?.[1]),_=Number(r?.b?.[0]),m=Number(r?.b?.[1]);if(![i,u,_,m].every(Number.isFinite))continue;const g=_-i,f=m-u,b=g*g+f*f;if(!(b>1e-18))continue;const v=Math.sqrt(b),y=h(n,[i,u],g,f,b),$=Math.hypot(n[0]-y.x,n[1]-y.y);if(!($=2?r.axisA:r.a,e=r.axisB?.length>=2?r.axisB:r.b,s=Number(e[0])-Number(t[0]),i=Number(e[1])-Number(t[1]),o=s*s+i*i;if(o>1e-18&&[t[0],t[1],e[0],e[1]].every(Number.isFinite)){const e=h(n,t,s,i,o),a=(n[0]-e.x)*w[0]+(n[1]-e.y)*w[1];if(a>l)k=2;else if(a<-1e-7){if(r.roomSide)continue;k=-2}else d&&(k=w[0]*d[0]+w[1]*d[1])}}else{let t=n[0]-y.x,e=n[1]-y.y;const s=Math.hypot(t,e);if(s>l)t/=s,e/=s,k=2;else if(d)[t,e]=d,k=1;else{const s=i<_||i===_&&u<=m;t=-(s?f:-f)/v,e=(s?g:-g)/v}w=[t,e]}const x=h([t,e],[i,u],g,f,b);let S=x.t,M=x.x,D=x.y;if(Number.isFinite(a)&&a>0){const t=Math.round(S*v/a)*a;S=Math.max(0,Math.min(1,t/v)),M=i+S*g,D=u+S*f}const C=ln(180*Math.atan2(-w[0],w[1])/Math.PI),P={cx:M+w[0]*(s/2),cy:D+w[1]*(s/2),angle:C,dist:$,sideScore:k,stableId:"string"==typeof r.stableId?r.stableId:""};(!p||P.distp.sideScore+l||Math.abs(P.sideScore-p.sideScore)<=l&&P.stableId.localeCompare(p.stableId)<0))&&(p=P)}return p&&{cx:p.cx,cy:p.cy,angle:p.angle,dist:p.dist}}const cn=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),dn=(t,e)=>{const s=t=>`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=s(t),o=s(e);return i<=o?`${i}|${o}`:`${o}|${i}`};function pn(t,e,s,i,o,a,n=1){const r=[],l=new Map;for(const h of t||[]){const c="string"==typeof h?.id?h.id:"";if(!c)continue;const d=K(t,c,e,s,i,o,a,n);if(d)for(let t=0;t1e-9))continue;const a=V(d.poly,t),n=Number(d.offsets[t]),h=Number.isFinite(n)&&n>0?n:0,p=[e[0],e[1]],u=[s[0],s[1]],_=dn(p,u),m=l.get(_)||new Set;m.add(c),l.set(_,m),r.push({a:[p[0]+a[0]*h,p[1]+a[1]*h],b:[u[0]+a[0]*h,u[1]+a[1]*h],axisA:p,axisB:u,normal:a,owner:"room",stableId:`room:${c}:${_}`,roomId:c,atomId:_,half:h})}}const h=[];for(const t of r){const{atomId:e,half:s,...i}=t;if(!(1===l.get(e)?.size&&s>1e-9)){h.push(i);continue}const o=i.normal;h.push({...i,roomSide:"inside",stableId:`${i.stableId}:inside`}),h.push({...i,a:[i.axisA[0]-o[0]*s,i.axisA[1]-o[1]*s],b:[i.axisB[0]-o[0]*s,i.axisB[1]-o[1]*s],normal:[-o[0],-o[1]],roomSide:"outside",stableId:`${i.stableId}:outside`})}return h}function un(t){const e=[],s=new Set;for(const i of t||[])if(Array.isArray(i)&&!(i.length<2))for(let t=0;t1e-9))continue;const n=[o[0],o[1]],r=[a[0],a[1]],l=`physical:${dn(n,r)}`;s.has(l)||(s.add(l),e.push({a:n,b:r,axisA:[...n],axisB:[...r],normal:null,owner:"physical",stableId:l}))}return e}const _n=new WeakMap;const mn=1e-9,gn=2e-7,fn=1e7,bn=t=>{const e=Math.sign(t)*Math.floor(Math.abs(t)*fn+.5)/fn;return Object.is(e,-0)?0:e},vn=t=>`${bn(t[0])},${bn(t[1])}`,yn=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),$n=(t,e,s)=>t/(e||1)*s,wn=(t,e)=>Math.atan2(e[1]-t[1],e[0]-t[0]),kn=t=>Array.isArray(t)&&t.length>=2&&t.every(t=>Number.isFinite(t)),xn=t=>(t||[]).filter(t=>kn(t?.a)&&kn(t?.b)&&yn(t.a,t.b)>mn);function Sn(t,{minAngleDeg:e=15,maxValence:s=6}={}){const i=new Map;for(const e of xn(t))for(const[t,s]of[[e.a,e.b],[e.b,e.a]]){const e=i.get(vn(t))||[];e.push(wn(t,s)),i.set(vn(t),e)}const o=[];for(const[t,a]of i){if(a.length>s&&o.push({rule:"valence",subject:t,actual:a.length,limit:s}),a.length<2)continue;const i=[...a].sort((t,e)=>t-e);let n=1/0;for(let t=0;tmn&&s(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI%180+180)%180,Dn=(t,e,s=1)=>{const i=Math.abs(Mn(t)-Mn(e));return Math.min(i,180-i)<=s},Cn=t=>{const e=new Map;for(const s of t)for(const t of[s.a,s.b]){const i=e.get(vn(t));i?i.push(s):e.set(vn(t),[s])}return e};function Pn(t,e,s){const i=xn(e),o=s??Cn(i),a=new Set([t]);let n=yn(t.a,t.b);const r=[t.a,t.b];for(;r.length;){const e=r.pop();for(const s of o.get(vn(e))||[])a.has(s)||Dn(s,t)&&Number(s.cm||0)===Number(t.cm||0)&&(a.add(s),n+=yn(s.a,s.b),r.push(s.a,s.b))}return n}function In(t,e,s,{minLengthCm:i=20}={}){const o=[],a=xn(t),n=Cn(a);for(const t of a){const r=Pn(t,a,n)/s*(e||1),l=Math.max(i,Number(t.cm)>0?Number(t.cm):0);r{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a<=mn?0:Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))};function zn(t,e,s,{minDistanceCm:i=5}={}){const o=xn(t),a=new Map;for(const t of o)a.set(vn(t.a),t.a),a.set(vn(t.b),t.b);const n=$n(i,e,s),r=n>mn?n:1,l=(t,e)=>`${Math.floor(t/r)},${Math.floor(e/r)}`,h=new Map;for(const[t,e]of a){const s=l(e[0],e[1]),i=h.get(s);i?i.push([t,e]):h.set(s,[[t,e]])}const c=new Map;for(const t of o){const e=Math.min(t.a[0],t.b[0])-n,s=Math.max(t.a[0],t.b[0])+n,i=Math.min(t.a[1],t.b[1])-n,o=Math.max(t.a[1],t.b[1])+n;for(let a=Math.floor(e/r);a<=Math.floor(s/r);a++)for(let e=Math.floor(i/r);e<=Math.floor(o/r);e++){const s=`${a},${e}`,i=c.get(s);i?i.push(t):c.set(s,[t])}}const d=[];for(const[t,o]of a){const a=Math.floor(o[0]/r),l=Math.floor(o[1]/r);for(let r=-1;r<=1;r++)for(let c=-1;c<=1;c++)for(const[p,u]of h.get(`${a+r},${l+c}`)||[]){if(t>=p)continue;const a=yn(o,u);a<=gn||a{const i=a[(s+1)%a.length];return t+(e[0]*i[1]-i[0]*e[1])},0))/2,r=(s||1)/i,l=n*r*r;return l0&&o.push(...e.slice(0,i))}return o}const Fn=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);function Nn(t){const{hass:e,devices:s,markers:i,showEntities:o,currentBinding:a,currentDeviceId:n,labels:r}=t,l=Z(i),h=new Set(i.filter(t=>t.removed).map(t=>t.binding)),c=new Set;for(const t of s)t.id!==n&&("device"===t.bindingKind&&t.bindingRef&&c.add(`device:${t.bindingRef}`),"entity"===t.bindingKind&&t.bindingRef&&c.add(`entity:${t.bindingRef}`));const d=new Set;for(const t of s)"device"===t.bindingKind&&t.name&&d.add(`${t.name.trim()}|${t.area||""}`);const p=[];for(const t of Object.values(e?.devices||{})){if(!t||"service"===t.entry_type)continue;const e=`device:${t.id}`;if(c.has(e))continue;const s=String(t.name_by_user||t.name||t.id).trim();e!==a&&!h.has(e)&&d.has(`${s}|${t.area_id||""}`)||p.push({value:e,label:s,sub:(t.model||r.device)+("Group"===t.model?r.z2mGroup:""),kind:"device",ref:t.id,areaId:t.area_id||"",model:t.model||""})}for(const[t,s]of Object.entries(e?.entities||{})){const i=`entity:${t}`;if(c.has(i))continue;if(Q(e,t,l)&&!h.has(i))continue;const o=Fn.has(s?.platform),a="group"===s?.platform;if(!o&&!a)continue;if(s?.hidden&&!h.has(i))continue;const n=e?.states?.[t];p.push({value:i,label:s?.name||n?.attributes?.friendly_name||t,sub:`${t.split(".")[0]} · ${a?r.group:r.helper}`,kind:"entity",ref:t,areaId:s?.area_id||s?.device_id&&e?.devices?.[s.device_id]?.area_id||"",model:"",parentDeviceId:s?.device_id||void 0})}if(o){const t=new Set(p.map(t=>t.value));for(const[s,i]of Object.entries(e?.entities||{})){const o=`entity:${s}`;if(c.has(o)||t.has(o)||i?.hidden&&!h.has(o))continue;const a=!!i?.device_id&&l.devices.has(i.device_id);if(Q(e,s,l)&&!h.has(o)&&!a)continue;const n=e?.states?.[s],d=i?.device_id?e?.devices?.[i.device_id]:null,u=d?String(d.name_by_user||d.name||""):"";p.push({value:o,label:i?.name||n?.attributes?.friendly_name||s,sub:`${s.split(".")[0]} · ${r.entity}${u?` · ${u}`:""}`,kind:"entity",ref:s,areaId:i?.area_id||d?.area_id||"",model:"",parentDeviceId:i?.device_id||void 0})}}return p.sort((t,e)=>t.label.localeCompare(e.label)||t.value.localeCompare(e.value))}const Hn={kind:"active",enabledEntityIds:[],allEntityIds:[]};function Ln(t){return t.startsWith("entity:")?"entity":"device"}function En(t){const e=t.binding.indexOf(":"),s=e>=0?t.binding.slice(e+1):"",i=Object.keys(t).filter(t=>!["id","binding","hidden"].includes(t));return t.id===`h${s}`&&0===i.length}const On=.001;function Bn(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function Wn(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function jn(t,e,s){return Math.abs(t[0]-e[0])"room"!==t.kind&&s.some(s=>s!==t&&function(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>s))return!1;const n=i/a,r=o/a,l=e=>Math.abs((e[0]-t.a[0])*r-(e[1]-t.a[1])*n);if(l(e.a)>s||l(e.b)>s)return!1;const h=(e.a[0]-t.a[0])*n+(e.a[1]-t.a[1])*r,c=(e.b[0]-t.a[0])*n+(e.b[1]-t.a[1])*r;return Math.min(a,Math.max(h,c))-Math.max(0,Math.min(h,c))>s}(t,s,e))),o=i.map(t=>{const[e,s]=Gn(t.a,t.b);return{a:e,b:s,key:`hidden|${Un(t,e,s)}`,sourceKind:t.kind,sourceId:t.id}}).sort((t,e)=>t.key.localeCompare(e.key)),a=i.flatMap(t=>[t.a,t.b].map((e,s)=>({point:[e[0],e[1]],key:`hidden|${qn(t)}|endpoint-${s}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:o,endpoints:a}}function Yn(t){const e=t.epsilon??On,s=t.roomCuts||[],i=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!Bn(e.a)||!Bn(e.b))continue;const t=i.get(e.hostId)||[];t.push([e.a[0],e.a[1],e.b[0],e.b[1]]),i.set(e.hostId,t)}const o=[];for(const[e,i]of tt(t.space.rooms).entries())i.length<4||o.push({a:[i[0],i[1]],b:[i[2],i[3]],kind:"room",id:`room-edge-${e}`,cuts:s});for(const e of t.space.room_drafts||[])if(e.id!==t.activeDraftId)for(let t=0;t+1Vn(s,t,e)))continue;const t=Wn(s);n.has(t)||n.set(t,{point:[s[0],s[1]],key:t})}}}return{segments:[...a.values()].sort((t,e)=>t.key.localeCompare(e.key)),endpoints:[...n.values()].sort((t,e)=>t.key.localeCompare(e.key))}}function Xn(t,e,s){return e.some(e=>Bn(e)&&jn(t,e,s))}function Zn(t,e,s){if(!s)return!0;const i=t-s.distance;return i<-1e-9||Math.abs(i)<=1e-9&&e.localeCompare(s.key)<0}function Qn(t,e,s){const i=s.epsilon??On,o=s.excludePoints||[],a=[...t.endpoints,...(s.extraEndpoints||[]).filter(t=>Bn(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],n=new Map;for(const t of a){if(Xn(t.point,o,i))continue;const e=Wn(t.point),s=n.get(e);(!s||t.key.localeCompare(s.key)<0)&&n.set(e,t)}return[...n.values()].map(t=>({kind:"endpoint",point:[...t.point],key:t.key,distance:Math.hypot(e[0]-t.point[0],e[1]-t.point[1])})).filter(t=>t.distance<=s.tolerance).sort((t,e)=>t.distance-e.distance||t.key.localeCompare(e.key))}function tr(t,e){if(!t.length)return null;if(e>0){const s=new Set;for(let i=0;i1)return{kind:"ambiguous",candidate:null,conflicts:t.filter(t=>s.has(t.key)).map(t=>({point:[...t.point],key:t.key}))}}return{kind:"resolved",candidate:t[0],conflicts:[]}}function er(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>0))return[...t.a];const n=i/a,r=o/a,l=Math.max(0,Math.min(a,(e[0]-t.a[0])*n+(e[1]-t.a[1])*r)),h=s>0?Math.max(0,Math.min(a,Math.round(l/s)*s)):l;return[t.a[0]+n*h,t.a[1]+r*h]}function sr(t,e,s){if(!(Bn(e)&&Bn(s.anchor)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??On,o=function(t,e){const s=e[0]-t[0],i=e[1]-t[1];if(!(Math.hypot(s,i)>Number.EPSILON))return null;const o=Math.round(Math.atan2(i,s)/(Math.PI/4))*(Math.PI/4);return[Math.cos(o),Math.sin(o)]}(s.anchor,e);if(!o)return{kind:"none",candidate:null,conflicts:[]};const a=Qn(t,e,s).filter(t=>function(t,e,s,i){const o=t[0]-e[0],a=t[1]-e[1];return o*s[0]+a*s[1]>i&&Math.abs(o*s[1]-a*s[0])<=i*Math.max(Math.hypot(o,a),1)}(t.point,s.anchor,o,i)),n=tr(a,s.distinguishTolerance||0);if(n)return n;let r=null;for(const a of t.segments){const t=a.b[0]-a.a[0],n=a.b[1]-a.a[1],l=a.a[0]-s.anchor[0],h=a.a[1]-s.anchor[1],c=o[0]*n-o[1]*t;let d=null;if(Math.abs(c)<=i*Math.max(Math.hypot(t,n),1)){if(Math.abs(l*o[1]-h*o[0])>i*Math.max(Math.hypot(l,h),1))continue;const t=[a.a,a.b].map(t=>({point:t,along:(t[0]-s.anchor[0])*o[0]+(t[1]-s.anchor[1])*o[1]})).filter(t=>t.along>i).sort((t,e)=>t.along-e.along);t.length&&(d=[...t[0].point])}else{const e=(l*n-h*t)/c,a=(l*o[1]-h*o[0])/c;e>i&&a>=-i&&a<=1+i&&(d=[s.anchor[0]+o[0]*e,s.anchor[1]+o[1]*e])}if(!d)continue;const p=Math.hypot(e[0]-d[0],e[1]-d[1]);p>s.tolerance||Xn(d,s.excludePoints||[],i)||!Zn(p,a.key,r)||(r={kind:"line",point:d,key:a.key,distance:p,segment:a})}return r?{kind:"resolved",candidate:r,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}const ir=.001;function or(t,e,s,i){const o=Number.isFinite(t)&&t>0?Math.floor(t):0,a=t=>"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,n=a(s),r=a(i)??0,l=[];let h=null;for(let t=0;t=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function rr(t,e,s,i){return t*i-e*s}function lr(t){let e=0;for(let s=0;st.localeCompare(e)),e[0]}function ur(t,e,s){const i=[...t];for(let t=!0;t&&i.length>=3;){t=!1;for(let o=0;o=0){i.splice(o,1),t=!0;break}}}return i}function _r(t,e){const s=t.map(t=>Math.max(0,Math.min(1,t))).sort((t,e)=>t-e),i=[];for(const t of s)(!i.length||Math.abs(t-i[i.length-1])>e)&&i.push(t);return i}function mr(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function gr(t,e,s){const i=function(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o:0}(t,e);if(i<-s||i>1+s)return null;const o=mr(e,i);return Math.hypot(o[0]-t[0],o[1]-t[1])<=s?Math.max(0,Math.min(1,i)):null}function fr(t,e,s,i,o){const a=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=e.b[0]-e.a[0],l=e.b[1]-e.a[1],h=e.a[0]-t.a[0],c=e.a[1]-t.a[1],d=rr(a,n,r,l),p=Math.max(Math.hypot(a,n),Math.hypot(r,l),1);if(Math.abs(d)>o*p){const t=rr(h,c,r,l)/d,e=rr(h,c,a,n)/d;return void(t>=-o&&t<=1+o&&e>=-o&&e<=1+o&&(s.push(Math.max(0,Math.min(1,t))),i.push(Math.max(0,Math.min(1,e)))))}if(!(Math.abs(rr(h,c,a,n))>o*p)){for(const i of[e.a,e.b]){const e=gr(i,t,o);null!=e&&s.push(e)}for(const s of[t.a,t.b]){const t=gr(s,e,o);null!=t&&i.push(t)}}}function br(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function vr(t,e){return t.minY-e.minY||t.index-e.index}function yr(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function $r(t){const e=t.right;return t.right=e.left,e.left=yr(t),yr(e)}function wr(t){const e=t.left;return t.left=e.right,e.right=yr(t),yr(e)}function kr(t,e){return t?(vr(e,t.item)<0?(t.left=kr(t.left,e),t.left.priority0)t.right=xr(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority=e&&Sr(t.left,e,s,i),t.item.minY<=s&&t.item.maxY>=e&&i.push(t.item),t.item.minY<=s&&Sr(t.right,e,s,i))}function Mr(t,e=.001){const s=Number.isFinite(e)&&e>0?e:ir,i=t.filter(t=>nr(t.a)&&nr(t.b)&&"string"==typeof t.key&&t.key.length>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>s),o=i.map(()=>[0,1]);!function(t,e,s){const i=t.map((t,e)=>({index:e,minX:Math.min(t.a[0],t.b[0])-s,maxX:Math.max(t.a[0],t.b[0])+s,minY:Math.min(t.a[1],t.b[1])-s,maxY:Math.max(t.a[1],t.b[1])+s})),o=[...i].sort((t,e)=>t.minX-e.minX||t.minY-e.minY||t.index-e.index),a=[...i].sort((t,e)=>t.maxX-e.maxX||t.index-e.index),n=new Set;let r=null,l=0;for(const i of o){for(;lt.index-e.index);for(const a of o)fr(t[a.index],t[i.index],e[a.index],e[i.index],s);n.add(i.index),r=kr(r,i)}}(i,o,s);const a=new Map;for(let t=0;t({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function Dr(t,e=.001){const s=Number.isFinite(e)&&e>0?e:ir,i=Mr(t,s),o=new Map,a=new Map,n=new Map(i.map(t=>[t.key,t]));for(const t of i){const e=cr(t.a,s),i=cr(t.b,s);o.set(e,t.a),o.set(i,t.b),a.has(e)||a.set(e,new Set),a.has(i)||a.set(i,new Set),a.get(e).add(i),a.get(i).add(e)}const r=new Map;for(const[t,e]of a){const s=o.get(t);r.set(t,[...e].sort((t,e)=>{const i=o.get(t),a=o.get(e);return Math.atan2(i[1]-s[1],i[0]-s[0])-Math.atan2(a[1]-s[1],a[0]-s[0])||t.localeCompare(e)}))}const l=new Set,h=new Map,c=(t,e)=>`${t}>${e}`;for(const t of i){const e=[cr(t.a,s),cr(t.b,s)];for(const[t,a]of[e,[e[1],e[0]]]){if(l.has(c(t,a)))continue;const e=[],d=[];let p=t,u=a,_=!1;for(let s=0;s<=2*i.length+2;s++){const s=c(p,u);if(l.has(s)){_=p===t&&u===a;break}l.add(s),e.push(p),d.push(dr(p,u));const i=r.get(u)||[],o=i.indexOf(p);if(o<0||!i.length)break;if(p=u,u=i[(o-1+i.length)%i.length],p===t&&u===a){_=!0;break}}if(!_||new Set(e).size<3||new Set(e).size!==e.length)continue;const m=e.map(t=>o.get(t)),g=lr(m);if(!(g>s*s))continue;const f=pr(ur(e,o,s)),b=new Set;for(const t of d)for(const e of n.get(t)?.sourceKeys||[])b.add(e);const v={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...b].sort((t,e)=>t.localeCompare(e))};h.has(f)||h.set(f,v)}}return{atoms:i,faces:[...h.values()].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))}}function Cr(t,e,s=.001){if(!nr(e))return null;const i=t.faces.filter(t=>{if(t.ring.some((i,o)=>function(t,e,s,i){const o=s[0]-e[0],a=s[1]-e[1],n=o*o+a*a;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1])<=i;const r=((t[0]-e[0])*o+(t[1]-e[1])*a)/n;return!(r<0||r>1)&&Math.hypot(e[0]+o*r-t[0],e[1]+a*r-t[1])<=i}(e,i,t.ring[(o+1)%t.ring.length],s)))return!1;let i=!1;for(let s=0,o=t.ring.length-1;se[1]!=n[1]>e[1]&&e[0]<(n[0]-a[0])*(e[1]-a[1])/(n[1]-a[1])+a[0]&&(i=!i)}return i});return[...i].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))[0]||null}function Pr(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(e[0]+i*n-t[0],e[1]+o*n-t[1])}function Ir(t,e,s=.5){let i=Math.abs(t-e)%180;return i>90&&(i=180-i),i<=s}function Tr(t,e,s,i){const o=t.filter(t=>"outer"===t.kind&&!t.open&&t.cm>0).map(t=>({interval:t,reusePartitionId:e.find(e=>function(t,e,s){return!(Math.abs(t.cm-e.cm)>1e-6)&&(Ir(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,.001)&&Pr(e.a,t.a,t.b)<=s&&Pr(e.b,t.a,t.b)<=s)}(e,t,i))?.id||null})),a=new Map;for(const e of s){if("partition"===e.host?.kind)continue;const s=t.map(t=>{const s=180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI;return{interval:t,distance:Ir(e.angle,s)?Pr([e.x,e.y],t.a,t.b):1/0}}).filter(t=>t.distance<=i).sort((t,e)=>t.distance-e.distance||t.interval.key.localeCompare(e.interval.key)),o=s[0]?.interval;"outer"===o?.kind&&!o.open&&o.cm>0&&a.set(e.id,o.key)}return{materialize:o,openingIntervals:a,removeOpeningIds:[...a.keys()].sort((t,e)=>t.localeCompare(e))}}function zr(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?Math.max(0,Math.min(1,((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o)):0}const Rr=t=>!t.key.startsWith("static:room|");function Ar(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;if(!(o>0))return null;const a=((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o,n=[e.a[0]+s*a,e.a[1]+i*a];return{point:n,t:a,distance:Math.hypot(n[0]-t[0],n[1]-t[1])}}function Fr(t,e){return e>0?Math.hypot(t[0]-Math.round(t[0]/e)*e,t[1]-Math.round(t[1]/e)*e):0}function Nr(t,e,s){if(Rr(t.source)!==Rr(e.source))return Rr(t.source)?t:e;const i=Fr(t.point,s),o=Fr(e.point,s);return Math.abs(i-o)>1e-9?i>o?t:e:function(t,e){return t[0]-e[0]||t[1]-e[1]}(t.point,e.point)>0?t:e}function Hr(t){return[t.from[0],t.from[1],t.to[0],t.to[1],t.targetKind].map(t=>"number"==typeof t?t.toFixed(6):t).join("|")}function Lr(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function Er(t,e){const s=e.epsilon??.001;if(!(e.maxDistance>=0)||!e.point&&!e.requiredSourceKey)return{kind:"none"};const i=new Map,o=t.flatMap(t=>[{source:t,endpoint:"a",point:t.a},{source:t,endpoint:"b",point:t.b}]);for(let t=0;ts)||l>e.maxDistance)continue;const h=Nr(n,r,e.gridStep||0),c=h===n?r:n;if(!Rr(h.source))continue;const d={sourceKey:h.source.key,endpoint:h.endpoint,from:[h.point[0],h.point[1]],to:[c.point[0],c.point[1]],targetSourceKey:c.source.key,targetKind:"endpoint",distance:l};i.set(Hr(d),d)}for(const a of o)if(Rr(a.source))for(const o of t){if(o===a.source)continue;const t=Ar(a.point,o);if(!t||t.t<=s||t.t>=1-s||!(t.distance>s)||t.distance>e.maxDistance)continue;const n={sourceKey:a.source.key,endpoint:a.endpoint,from:[a.point[0],a.point[1]],to:t.point,targetSourceKey:o.key,targetKind:"line",distance:t.distance};i.set(Hr(n),n)}const a=[];for(const o of i.values()){const i=Dr(Lr(t,o),s),n=e.point?Cr(i,e.point,s):i.faces.find(t=>!e.requiredSourceKey||t.sourceKeys.includes(e.requiredSourceKey))||null;n&&a.push({proposal:o,face:n})}if(a.sort((t,e)=>t.proposal.distance-e.proposal.distance||Hr(t.proposal).localeCompare(Hr(e.proposal))),!a.length)return{kind:"none"};return new Set(a.map(t=>`${Hr(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:a.map(t=>t.proposal)}:{kind:"repair",...a[0]}}const Or=(t,e)=>(t.composedPath?.()||[t.target]).some(t=>(t=>!!t&&"function"==typeof t.matches)(t)&&e(t));class Br{constructor(t){this.host=t,this._openGroupId=null,this._groupGeneration=0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,this._globalDismissListening=!1,this._globalDismissGuard=t=>{this.handleOutsideDismiss(t)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(t){return this._openGroupId&&t.find(t=>t.id===this._openGroupId)||null}reset(){this._openGroupId=null,this._contentAnimation?.cancel(),this._contentAnimation=void 0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}closeForNavigation(){const t="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,e=!!this._openGroupId||!!t;this._openGroupId=null,t?.(),t&&(this._currentModel=null),this._clearDismissClick(),e&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(t,e){this._openGroupId!==e?t.some(t=>t.id===e)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=e,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(t){const e=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),t&&e&&this.host.updateComplete().then(()=>{const t=this.host.root(),s=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e&&null!==t.offsetParent);(s||t.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(t,e,s){const i=this._openGroupId===t.id,o=t.items.find(e=>e.id===t.activeItemId&&!e.disabled);return p``}renderGroupModel(t,e,s){const i=t.items.find(t=>!t.disabled)?.id,o=t.items.some(e=>e.id===t.activeItemId&&!e.disabled)?t.activeItemId:void 0;return{contextId:e,kind:"group",ariaLabel:s.openGroup(t.label),visibleLabel:t.label,content:p`
    this._groupKeydown(t)}> + ${t.items.map(e=>p``)} +
    `}}runContext(t,e,s){if(t===e)return s()}render(t,e){this._currentModel=t,this._blocked=e;const s=t?.kind||"hidden";return p`
    + +
    `}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(Or(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=Or(t,t=>t.classList.contains("editor-secondary")),i=Or(t,t=>!!t.dataset.editorGroup),o=Or(t,t=>!!t.dataset.editorPalette),a=Or(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const Wr=t=>t.stopPropagation(),jr=1e-6*W,Gr=6*W;function qr(t){return Number.isFinite(t)?Math.round(t/W)*W:t}const Ur=(t,e,s,i)=>Math.hypot(s-t,i-e),Kr=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),Vr=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,l=Math.PI/180,h=Math.cos(s*l)*r,c=Math.sin(s*l)*r,d=Math.cos(n*l)*r,p=Math.sin(n*l)*r,u=Math.max(Ur(t+h,e+c,o+d,a+p),Ur(t-h,e-c,o-d,a-p)),_=Math.max(Ur(t+h,e+c,o-d,a-p),Ur(t-h,e-c,o+d,a+p));return Math.min(u,_)},Jr=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function Yr(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,l="",h=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=jr&&c++},u=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},_={};let m=5;for(const t of s){const e=Jr(t);null!=t?.id&&(_[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>jr||i))return;o++,t>n&&(n=t);const a=t*ot*e;a>r&&(r=a,l=s)};for(const t of s){const e=Jr(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[qr(e[0]),qr(e[1])];return t=Math.max(t,Ur(e[0],e[1],s[0],s[1])),u(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=qr(e+o),r=qr(s+a),l=qr(e),h=qr(s),c=Math.max(W,n-l),d=Math.max(W,r-h);t=Kr(e,s,o,a,l,h,c,d),i.x=l,i.y=h,i.w=c,i.h=d,p(e,l),p(s,h),p(e+o,l+c),p(s+a,h+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[qr(e[0]),qr(e[1])];return t=Math.max(t,Ur(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],l=n.length?[[o[0],n[0]]]:[],h=[];for(let t=0;t+1=2)for(const[t,e]of l)u(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[qr(o[0]),qr(o[1])],l=[qr(n[0]),qr(n[1])];let h=Math.max(Ur(o[0],o[1],r[0],r[1]),Ur(n[0],n[1],l[0],l[1]));const c=Ur(r[0],r[1],l[0],l[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-jr&&i+e/2<=c+jr});c>jr&&d?(i.a=r,i.b=l,u(o,r),u(n,l)):h=0,g(h,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[qr(t[0]),qr(t[1])],n=Ur(t[0],t[1],o[0],o[1]);i.center=o,u(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[qr(e[0]),qr(e[1])],a=[qr(s[0]),qr(s[1])];t=Math.max(Ur(e[0],e[1],o[0],o[1]),Ur(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],u(e,o),u(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=qr(e),r=qr(s);if(null!=i.w&&null!=i.h){const l=qr(e+o),h=qr(s+a),c=Math.max(W,l-n),d=Math.max(W,h-r);t=Kr(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=Ur(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=jr)continue;const l=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),u=!(Number.isFinite(p)&&p===d),_=Vr(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,l,c,d);i.x=l,i.y=c,i.angle=d,u&&h++,g(_,e,s,u);continue}const o=it([i.x,i.y],t.rooms||[],Gr,{step:W,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),l=Vr(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&h++,g(l,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=qr(s.x),n=qr(s.y),r=Ur(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const l="string"==typeof s.s?s.s:"";g(r,_[l]??m,l)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:l,rotated:h,removedDrafts:d},changed:o>0||c>0}}function Xr(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const Zr=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Qr=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function tl(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return Qr(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function el(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&Zr(i.a)&&Zr(i.b)&&(Qr(t,i.a)<=o||Qr(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(Qr(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function il(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&Zr(t.a)&&Zr(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tQr(e,t.at)>s);let c=h[0]??r.a,d=h[h.length-1]??l.b;if(h.length<2&&(c=r.a,d=l.b),d[0]0?(d[0]-c[0])/u:0,m=u>0?(d[1]-c[1])/u:0,g=t=>u>0?((t[0]-c[0])*_+(t[1]-c[1])*m)/u:0;for(const t of[r,l]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function ol(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:Xr(t.t,n)},a++;const r=nt(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,rt(s,r,i.coordScale))}return a}function al(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>at(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const nl=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),rl=(t,e)=>t[0]-e[0]||t[1]-e[1],ll=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],hl=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,cl=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const l=a/r,h=n/r,c=e=>Math.abs((e[0]-t[0])*h-(e[1]-t[1])*l);if(c(s)>o||c(i)>o)return null;const d=hl(s,t,l,h),p=hl(i,t,l,h),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>o?{lo:u,hi:_}:null},dl=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,pl=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},ul=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,l=a/n,h=x(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!nl(o))return!0;const a=hl(o,t.a,r,l),c=Math.abs((o[0]-t.a[0])*l-(o[1]-t.a[1])*r),d=x(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=h+d+i})},_l=(t,e,s)=>{if(t.host){const i=nt(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},ml=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),l=Math.sin(n);if(Math.abs(o*l-a*r)>1e-6)return!1;const h=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(h*a-c*o)>s)return!1;const d=h*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),fl=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},bl=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function vl(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],l=Array.isArray(t?.room_drafts)?t.room_drafts:[],h={walls:a,partitions:n,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!l.length)return h;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,u=r;const _=lt(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const l=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(l>i)){e=!1;break}const h=s.flatMap(t=>{const e=t.cm>0?t.cm:ht;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of h){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=l-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!pl(t))continue;const s=v.get(t.id);if(!(s&&nl(s.a)&&nl(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=rl(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],l=n[1]-a[1],h=Math.hypot(r,l);if(!(h>c))continue;const _=r/h,g=l/h,y=lt(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=cl(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=u.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:nt(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,h];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=hl(e.center,a,_,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(h,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=h+c).map(t=>Math.max(0,Math.min(h,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>gl(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=ll(a,_,g,i),l=ll(a,_,g,n),h=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of h)d.set(t.roomId,t);const p=[...d.values()],u=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:ht)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===u.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:l,cm:s.cm},w=D.some(t=>!!cl(r,l,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:ht:0,I=v?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:l,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tdl(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&ct(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const l=dt(s,o.cellCm,o.gridPitch),h={lo:i.lo-l,hi:i.hi+l};for(const t of P)dl(t,h,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>dl(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=dt(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)dl(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const z=bl(P,c),R=z.filter(t=>t.safe),A=z.filter(t=>!t.safe);if(!R.length)continue;if(p.length-1+A.length>2e3)continue;let F=d;for(const t of R){const[e,i]=rl(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];F=pt(F,e,i,t.finalCm,o.pitch,o.coordScale)}if(F=ut(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),F.length>500)continue;const N=new Set(p.map(t=>t.id));N.delete(s.id);const H=A.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:fl(s.id,i,a,N);return N.add(n),{id:n,a:i,b:a,cm:s.cm}}),L=p.findIndex(t=>t.id===s.id),E=p.slice();E.splice(L,1,...H);const O=E.map(t=>gl(t,o.coordScale)),B=new Map;let W=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=rt(t,s.resolved,o.coordScale),{host:i,...a}=e;B.set(t.id,a);continue}const i=S.get(t.id),a=H[A.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&O.find(t=>t.id===a.id);if(!a||!n){W=!1;break}const r=n.b[0]-n.a[0],l=n.b[1]-n.a[1],h=Math.hypot(r,l),d=hl(e.center,n.a,r/h,l/h),p={...rt(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/h}};if(!_t(p,O,o.coordScale,o.cellCm,o.gridPitch).resolved){W=!1;break}B.set(t.id,p)}if(!W)continue;const j=u.map(t=>B.get(t.id)||t),G=[...I.keys()].map(t=>B.get(t)),q=G.map(t=>_l(t,O,o));if(q.some(t=>!t))continue;let U=!1;for(let t=0;tt.id));for(const t of j){if(K.has(t.id))continue;const e=_l(t,O,o);e&&q.some(t=>ml(t,e,c))&&(U=!0)}if(U)continue;const V=mt(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=B.get(t),i=gt(V,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=F,p=E,u=j,f+=R.length,b+=I.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const yl=t=>JSON.parse(JSON.stringify(t)),$l=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),wl=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function kl(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const xl=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const Sl=1e-9;class Ml extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const Dl=t=>JSON.parse(JSON.stringify(t)),Cl=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),Pl=t=>[Number(t[0]),Number(t[1])],Il=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,Tl=(t,e)=>{const s=Il(t),i=Il(e);return sIl(t)<=Il(e)?[Pl(t),Pl(e)]:[Pl(e),Pl(t)],Rl=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),Al=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/W;Math.abs(i-Math.round(i))>=kt&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of at(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},Nl=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},Hl=(t,e,s)=>{const i=Math.max(0,Math.min(1,Nl(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},Ll=(t,e,s,i,o=Sl)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const l=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(l(s)>o||l(i)>o)return 0;const h=Nl(s,t,e),c=Nl(i,t,e);return Math.max(0,Math.min(1,Math.max(h,c))-Math.max(0,Math.min(h,c)))*r},El=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),l=new Uint32Array(64),h=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=h(s,17)^h(s,19)^s>>>10;l[t]=l[t-16]+i+l[t-7]+o>>>0}let[e,s,i,o,c,d,p,u]=r;for(let t=0;t<64;t++){const a=u+(h(c,6)^h(c,11)^h(c,25))+(c&d^~c&p)+n[t]+l[t]>>>0,r=e&s^e&i^s&i;u=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((h(e,2)^h(e,13)^h(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+u>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},Bl=(t,e,s,i,o="")=>{const[a,n]=zl(e,s),r=`${t}|${Il(a)}|${Il(n)}|${[...i].sort().join(",")}${o}`;return`wall-${Ol(El(r)).slice(0,20)}`};let Wl=0;const jl=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||Ol(El(`${Date.now()}|${Wl++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new Ml("duplicate-id","id factory exhausted")},Gl=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&Cl(s.a)&&Cl(s.b)){if(e.has(s.id))throw new Ml("duplicate-id",s.id);e.set(s.id,s)}return e},ql=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=Sl&&Math.abs(e[1]-i[1]-a)<=Sl?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},Ul=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=yt(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:$t(s,null,1,.04*W,!0),a=[];for(const t of s){const s=at(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>Hl([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*W)):[],l=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),h=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!xt(t,e,Number(s.angle)))return!1;if(Hl(i,t,e)>.02*W)return!1;const o=Rl(t,e),a=Nl(i,t,e)*o,n=Number(s.length)/2;return a+n>=-Sl&&a-n<=o+Sl}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=at(i);if(!r||!p||p.length<3)throw new Ml("invalid-room",r);const u=wt(s,r,n,W,1,[...t.walls||[],...l]);if(!u||u.poly.length<3)throw new Ml("invalid-room",r);const _=Array.isArray(i.wall_ids)?i.wall_ids:[],m=_.length===p.length;let g=null,f=m;if(f)for(let t=0;tSl||Math.abs(i[1]-g[1])>Sl)){f=!1;break}g=i}const b=[];for(let t=0;t2)throw new Ml("third-owner",n);const d=m?_[u.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||Ll(s,i,o.a,o.b)>Sl?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:Pl(p[u.parent[t]]),b:Pl(p[(u.parent[t]+1)%p.length])})}const g=u.parent[t];l.parentKeys.add(vt(p[g],p[(g+1)%p.length],W));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>Hl(v,[t[0],t[1]],[t[2],t[3]])<=.04*W);l.zeroWall||=y(a)||y(o)&&!h(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:u.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},Kl=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[Tl(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new Ml("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new Ml("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],l=i?s.get(i):void 0;if(l){r.set(t,l);continue}const h=i?t.preferredCarriers.get(i):void 0;if(i&&h){r.set(t,{id:i,a:h.a,b:h.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>Ll(t.a,t.b,e.a,e.b)>Sl),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||Rl(e.a,e.b)-Rl(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const l=new Map;for(const[t,e]of r){const s=l.get(e.id)||[];s.push(t),l.set(e.id,s)}for(const[t,e]of l){const i=s.get(t)||r.get(e[0]);if(!i)throw new Ml("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=Hl(o,t.a,t.b)<=Sl?0:1,a=Hl(o,e.a,e.b)<=Sl?0:1;if(s!==a)return s-a;return(Hl(i.a,t.a,t.b)<=Sl?0:1)-(Hl(i.a,e.a,e.b)<=Sl?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const h=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(h.has(t.id))throw new Ml("duplicate-id",t.id);h.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=zl(e.a,e.b),o=`${String(t.id||"")}|${Il(s)}|${Il(i)}|${[...e.owners].sort().join(",")}`,a=Ol(El(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new Ml("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;h.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,h.add(o)}}else for(const t of c)t.id=jl(h),h.add(t.id)},Vl=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=Nl(s,e.a,e.b),o=Rl(e.a,e.b),a=Number(t.length)/2;return i>=-Sl&&i<=1+Sl&&Hl(s,e.a,e.b)<=.02*W&&xt(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-Sl&&i*o+a<=o+Sl},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,Nl(s,n.a,n.b)))}},Jl=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=Nl(s,e.a,e.b),a=Rl(e.a,e.b);return o>=-Sl&&o<=1+Sl&&Hl(s,e.a,e.b)<=.02*W&&xt(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-Sl&&o*a+i<=a+Sl}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>Hl(s,t.a,t.b)-Hl(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,Nl(s,a.a,a.b)))}:null},Yl=(t,e,s)=>{const i=Gl(t),{atoms:o,rooms:a}=Ul(t,i);Kl(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=vt(e.a,e.b,W),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!Cl(t.a)||!Cl(t.b))&&Ll(e.a,e.b,t.a,t.b)>=Rl(e.a,e.b)-Sl)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new Ml("thickness-conflict",e.key);if(a.length)return a[0];const n=St(i,e.a,e.b,W,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:vt(t.a,t.b,W),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=Vl(t,e);s&&(t.host=s);continue}const i=Vl(t,e)??(s?Jl(t,e):null);if(i)t.host=i;else{if(!s)throw new Ml("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function Xl(t,e={}){const s=JSON.stringify(t),i=Dl(t);bt(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=Yl(t,a,s)}return i.model_version=9,bt(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const Zl=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function Ql(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)Zl(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(Zl(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):Dl(e)}return o[e,e in t?s(t[e],i):Dl(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return Dl(e)};return s(t,e)}const th=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),eh=(t,e,s)=>Math.min(s,Math.max(e,t)),sh=t=>Et({spaces:[t]})[0],ih=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,l=Math.max(i*r*.02,1e-9),h=[];for(const e of t||[])for(const t of at(e)||[])h.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l),p=new Map;for(const n of t||[]){if(!n?.id)continue;const u=K(t,n.id,e,s,i,o,a,r);if(u)for(let t=0;t0&&_>0&&n===m&&_!==n))continue;const g=u.poly[i],f=u.poly[(i+1)%u.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>l&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,h)&&d(f,h))continue;const v=ih(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let u=e.slice();const _=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(_(s,e.a)&&_(i,e.b)||_(s,e.b)&&_(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>ih(t.a,t.b,r).localeCompare(ih(e.a,e.b,r)))){const e=[...t.targets][0],s=u.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;u=u.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(u=pt(u,t.a,t.b,e,i,r))}return u}const ah=t=>{const e=[];for(const s of t||[]){const t=at(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(h),d=new Map;for(const t of h){const e=kl("space",t);e.layers>0&&wl("space",e.root)&&xl(d,e.root,t)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=kl("room",t);o.layers>0&&wl("room",o.root)&&xl(s,o.root,t),u.has(t)||u.set(t,e),_.has(t)||_.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(u.keys()),f=new Map,b=new Map;for(const t of l)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=kl("space",t).root;if(!wl("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=kl("room",t).root;if(!wl("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of l){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),l=!0===t.removed;let h=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,h=i,p=!0,v.spaceRefsRemapped++):l||"virtual"===t.binding?l||(delete t.space,h=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,h=d.spaceId,v.spaceRefsRemapped++):(delete t.space,h=null,v.markersDetached++))}l&&!p||x(t,h,d),r&&p&&n===s&&h&&(o[e]={...a,s:h},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;$l(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),z=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},R=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),l=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(l)a=T(t,i,"device",String(l.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",_.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,z(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,R.add(i)}else delete o[t],z(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...R].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=Mt(n,a.layout);bt(n),Dt(a.layout);const l=JSON.stringify(t||{}),h=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&th(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(th(o,"segments")&&(delete o.segments,e++),th(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=Rt&&t<=At&&(th(o,"plan_scale_x")||(o.plan_scale_x=t),th(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);th(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?eh(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)th(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&th(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(th(t,"width")){const s=Number(t.width);(th(t,"width_cm")||Number.isFinite(s))&&(th(t,"width_cm")||(t.width_cm=Number(eh(s/Tt*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(th(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),th(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(eh(Ft*Nt(t)/Tt*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(th(t,"scale")||th(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Ht(t[1]));if(!(th(t,"entity")||th(t,"attr")||th(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":Lt(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const u=(_=n.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=Yr(n.spaces||[],a.layout);let g=0,f=0,v=0,y="";const $=m.spaces.map(t=>{const e=Ct(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/W*s;return i>v&&(v=i,y=String(t?.id||"")),e.space}),w=g?Yr($,m.layout):{...m,spaces:$};n.spaces=w.spaces;const k=g?{...m.report,moved:m.report.moved+w.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+w.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,w.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,w.report.maxShiftCm),maxSpace:w.report.maxShiftCm>m.report.maxShiftCm?w.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+w.report.rotated,removedDrafts:m.report.removedDrafts+w.report.removedDrafts}:{...m.report};let x=0,S=0,M=0,D=0,C=0,P=0,I=0,T=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=sh(e),r=sh(s);if(!a||!r)continue;const l=ah(a.rooms),h=ah(r.rooms),c=.02*Tt,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:vt(t.a,t.b,W),cm:0})),p=new Set(d.map(t=>t.key)),_=Pt(e,a.rooms,b,c);S+=_.length;for(const t of _){const e=[t[0]/b,t[1]/b],s=[t[2]/b,t[3]/b],i=vt(e,s,W);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=It(d,l,h,W,b),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},v=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*b),s=t.b.map(t=>t*b);return h.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});M+=Math.max(0,m-v.length);const y=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...y,...v.map(({key:t,...e})=>e)];const $=v.map(t=>[t.a[0]*b,t.a[1]*b,t.b[0]*b,t.b[1]*b]),w=Array.isArray(e.walls)?e.walls.length:0;let k=It(e.walls,l,h,W,b);k=oh(r.rooms,k,$,W,Number(s.cell_cm)>0?Number(s.cell_cm):5,Tt,b),k=ut(r.rooms,k,$,W,Number(s.cell_cm)>0?Number(s.cell_cm):5,Tt,b),k=zt(k,s.rooms||[],W,1,$.map(t=>[t[0]/b,t[1]/b,t[2]/b,t[3]/b])),x+=Math.max(0,w-k.length),k.length?s.walls=k:delete s.walls;const z=il(s.partitions||[],{pitch:W,geometry:al(s)});z.merged&&(D+=z.merged,s.partitions=z.partitions,ol(s.openings,s.partitions,z.openingMoves,{coordScale:b,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:Tt}));const R=sh(s);if(R){const t=o(s,R,s.walls||[],$,{pitch:W,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:Tt,coordScale:b});t.partitionsReconciled&&(C+=t.partitionsReconciled,P+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(I+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&T++}let z=0;c<=9&&(z=function(t){const e=Xl(t);return Ql(t,e.config),{...e,config:t}}(n).migratedSegments);const R=bt(n),A=Dt(w.layout),F=JSON.stringify(R)!==l||JSON.stringify(A)!==h;c<9&&F&&(R.model_version=9);const N=JSON.stringify(R)!==l||JSON.stringify(A)!==h,H=Number.isInteger(Number(R.model_version))?Number(R.model_version):c,L=N?k:{...k,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},E=N?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:R,layout:A,report:{...L,modelFrom:c,modelTo:H,migrated:N?p:0,glowSpacesMigrated:N?d.glowSpaces:0,glowRoomsMigrated:N?d.glowRooms:0,canonicalized:N?T:0,wallSegmentsMigrated:N?z:0,legacyZeroWallsMigrated:N?S:0,wallsMerged:N?x:0,spansMerged:N?M:0,partitionsMerged:N?D:0,partitionsReconciled:N?C:0,openingsRehosted:N?P:0,redundantDraftsRemoved:N?I:0,wallsStraightened:N?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:N?v:0,maxStraightenSpace:N?y:"",latticeCoordinatesCanonicalized:N?r.canonicalized:0,latticeCoordinatesFar:N?r.far:0,latticeMaxShift:N?r.maxShift:0,latticeMaxShiftCm:N?r.maxShiftCm:0,latticeSpaces:N?r.spaces:[],...E},changed:N}}const rh=new WeakSet,lh=t=>{t._regSignature="",t._maybeRebuildDevices(),t.requestUpdate()};async function hh(t,e,s,i,o,a=""){const n=t.host,r=n._serverCfg;if(!r)return!1;let l=JSON.parse(JSON.stringify(r));l.markers=l.markers||[];let h=l.markers.find(t=>t.id===e);if(!h){const t=n._devices.find(t=>t.id===e);if(!t||"device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return!1;h={id:t.id,binding:`${t.bindingKind}:${t.bindingRef}`,space:t.space||null,area:t.area||null,hidden:!!t.hidden},l.markers.push(h)}h.vacuum=function(t,e){const s=e.matrix.map(t=>Number(t.toFixed(6))),i=t?.map_routes;if(Array.isArray(i)&&i.length){const o=i.find(t=>t.id===e.routeId)||i.find(t=>t.source===e.source&&t.map_id===e.mapId);if(o)return{...t,map_routes:Do(i,o.id,s)}}return{...t,source:e.source,calibration:{...t?.calibration||{},[e.mapId]:s}}}(h.vacuum||{},{source:s,mapId:i,routeId:a,matrix:o}),l=t._prepareConfigCandidate(l);const c=u(r,l,n._cfgContentFingerprint,n._cfgRev,_);n._serverCfg=l,lh(n),n._saveConfigDebounced.pending()&&n._saveConfigDebounced.cancel();try{return await t._saveConfigNow(c),!0}catch(t){return m(n,c,_),lh(n),n._showToast(n._t("toast.cfg_save_failed",{err:n._errText(t)})),!1}}function ch(t){return 1===t}function dh(t){return`${t}-${globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`}function ph(t){const e=function(t){return JSON.stringify({message:t.message.trim(),contact:t.contact.trim(),previewToken:t.attach&&t.preview?t.preview.token:""})}(t);return{idempotencyKey:t.submissionFingerprint&&t.submissionFingerprint!==e?dh("report"):t.idempotencyKey,fingerprint:e}}function uh(t){return[...t].length}function _h(t,e=Date.now()){const s=t.message.trim();return s?uh(s)>1e4?"message_too_long":uh(t.contact.trim())>320?"contact_too_long":t.attach&&!t.preview?"preview_missing":t.attach&&t.preview.expiresAt<=e?"preview_expired":null:"message_required"}function mh(t,e=Date.now()){return"building"!==t.status&&"sending"!==t.status&&"success"!==t.status&&null===_h(t,e)}function gh(t){const e=function(t){const e=/Firefox\/(\d+)/i.exec(t);if(e)return{family:"firefox",major:Number(e[1])||0};const s=/(?:Chrome|Chromium|Edg)\/(\d+)/i.exec(t);if(s)return{family:"chromium",major:Number(s[1])||0};const i=/Version\/(\d+).+Safari\//i.exec(t);return i?{family:"webkit",major:Number(i[1])||0}:{family:"unknown",major:0}}(t.userAgent??globalThis.navigator?.userAgent??""),s=t.now??Date.now(),i=Number(t.registryLastSuccess||0),o=i<=0?"unknown":s-i<=6e5?"fresh":"stale",a=["en","ru","de","fr"].includes(t.language)?t.language:"en",n=globalThis.matchMedia?.bind(globalThis);return{browser_family:e.family,browser_major:Math.max(0,Math.min(999,e.major)),language:a,coarse_pointer:t.coarsePointer??!!n?.("(pointer: coarse)").matches,hover_capable:t.hoverCapable??!!n?.("(hover: hover)").matches,registry_access:"full"===t.registryAccess?"full":"limited"===t.registryAccess?"partial":"unavailable",registry_age_bucket:o}}function fh(t){const e=t&&"object"==typeof t&&"code"in t?String(t.code||""):"";return new Set(["support_invalid_message","support_preview_expired","support_package_too_large","support_rate_limited","support_unavailable","support_rejected","unauthorized"]).has(e)?e:"support_unavailable"}const bh=[{id:"air_conditioner",group:"appliance",art:{d:"M20.5 39h69a2.5 2.5 0 0 1 2.5 2.5V69a2.5 2.5 0 0 1-2.5 2.5h-69A2.5 2.5 0 0 1 18 69V41.5a2.5 2.5 0 0 1 2.5-2.5 M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v8.5H25z M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v3.5H25z",viewW:110,viewH:110}},{id:"armchair",group:"furniture",art:{d:"M42.5 29h25a10 10 0 0 1 10 10v26.5a10 10 0 0 1-10 10h-25a10 10 0 0 1-10-10V39a10 10 0 0 1 10-10 M77.5 48.5a6 6 0 0 1 2 11.657V71.5a4 4 0 0 1-4 4H34a4 4 0 0 1-4-4V60.157A6.001 6.001 0 0 1 32 48.5a6 6 0 0 1 5.419 3.424A4 4 0 0 1 38 54v7h33.5v-6.5q0-.107.005-.213A6 6 0 0 1 77.5 48.5 M42.75 57h24.5A4.75 4.75 0 0 1 72 61.75a4.75 4.75 0 0 1-4.75 4.75h-24.5A4.75 4.75 0 0 1 38 61.75 4.75 4.75 0 0 1 42.75 57M36.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 41.392 82H38a1.5 1.5 0 0 1-1.5-1.5zm36 0h-8l1.674 5.441A1.5 1.5 0 0 0 67.608 82H71a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"bathtub",group:"sanitary",art:{d:"M28 75h8l-1.674 5.441a1.5 1.5 0 0 1-1.434 1.059H29.5A1.5 1.5 0 0 1 28 80zm53 0h-8l1.674 5.441a1.5 1.5 0 0 0 1.434 1.059H79.5A1.5 1.5 0 0 0 81 80zM19.5 46.5H90a2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5H19.5A2.5 2.5 0 0 1 17 49a2.5 2.5 0 0 1 2.5-2.5 M19.5 51.5H90L86.633 68a10 10 0 0 1-9.798 8h-44.17a10 10 0 0 1-9.798-8zM30 36v-2a5 5 0 1 0-10 0v12.5",viewW:110,viewH:110}},{id:"bed",group:"furniture",art:{d:"M16 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 20.892 82H17.5a1.5 1.5 0 0 1-1.5-1.5zm78.5 0h-8l1.674 5.441A1.5 1.5 0 0 0 89.608 82H93a1.5 1.5 0 0 0 1.5-1.5zM25 29h60.5a2.5 2.5 0 0 1 2.5 2.5V68a2.5 2.5 0 0 1-2.5 2.5H25a2.5 2.5 0 0 1-2.5-2.5V31.5A2.5 2.5 0 0 1 25 29 M16 59.5c0-5.523 4.477-10 10-10h58.5c5.523 0 10 4.477 10 10v6H16zm-1 6h80.5a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H15a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1m15.5-20a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4h-21zm28.5 0a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4H59z",viewW:110,viewH:110}},{id:"bidet",group:"sanitary",art:{d:"M36 57.5c0 5.523 4.477 10 10 10h18.5c5.523 0 10-4.477 10-10V53H36zm8.5 22a1 1 0 0 0 1 1H65a1 1 0 0 0 1-1v-12H44.5zm-8.5-32a1 1 0 0 1 1-1h36.5a1 1 0 0 1 1 1V53H36zM50.5 41a1 1 0 0 1 1-1H59a1 1 0 0 1 1 1v5.5h-9.5zM53 30a1 1 0 0 1 1-1h2.5a1 1 0 0 1 1 1v10H53zm-4 6a1 1 0 0 1-1-1v-2.5a1 1 0 0 1 1-1h4V36z",viewW:110,viewH:110}},{id:"boiler",group:"appliance",art:{d:"M46.5 81.5h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zm12.5 0h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zM44 23h22.5a8 8 0 0 1 8 8v42.5a8 8 0 0 1-8 8H44a8 8 0 0 1-8-8V31a8 8 0 0 1 8-8 M36 31a8 8 0 0 1 8-8h22.5a8 8 0 0 1 8 8v42H36z M48.5 42.5a7 7 0 1 0 14 0 7 7 0 1 0-14 0m6.5 0 3.5-3.5",viewW:110,viewH:110}},{id:"chair",group:"furniture",art:{d:"M40.5 39.5C40.5 32.596 46.096 27 53 27h4c6.904 0 12.5 5.596 12.5 12.5v13a5 5 0 0 1-5 5h-19a5 5 0 0 1-5-5z M41.25 54h27.5A3.25 3.25 0 0 1 72 57.25a3.25 3.25 0 0 1-3.25 3.25h-27.5A3.25 3.25 0 0 1 38 57.25 3.25 3.25 0 0 1 41.25 54m28.25 6.5H64l1.88 21.63a1.5 1.5 0 0 0 1.495 1.37H68a1.5 1.5 0 0 0 1.5-1.5zm-29 0H46l-1.88 21.63a1.5 1.5 0 0 1-1.495 1.37H42a1.5 1.5 0 0 1-1.5-1.5z",viewW:110,viewH:110}},{id:"coffee_table",group:"furniture",art:{d:"M25 51h5.238l-1.534 15.646A1.5 1.5 0 0 1 27.211 68H26.5a1.5 1.5 0 0 1-1.5-1.5zm60.238 0H80l1.534 15.646A1.5 1.5 0 0 0 83.027 68h.711a1.5 1.5 0 0 0 1.5-1.5z M24.34 43.485A2.5 2.5 0 0 1 26.625 42h56.75a2.5 2.5 0 0 1 2.285 1.485l2.715 6.109A1 1 0 0 1 87.461 51H22.54a1 1 0 0 1-.914-1.406z M87.5 51h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"computer",group:"appliance",art:{d:"M24.5 38h40a2.5 2.5 0 0 1 2.5 2.5v22a2.5 2.5 0 0 1-2.5 2.5h-40a2.5 2.5 0 0 1-2.5-2.5v-22a2.5 2.5 0 0 1 2.5-2.5m50 0h11a2.5 2.5 0 0 1 2.5 2.5v29a2.5 2.5 0 0 1-2.5 2.5h-11a2.5 2.5 0 0 1-2.5-2.5v-29a2.5 2.5 0 0 1 2.5-2.5M43 65h5v7h-5zm-6 7h16m23-12h8m-8-17h8 M35 56V46h5m-5 7h5 M39.5 53a3.5 3.5 0 1 0 0-7m15.036 8.536a5 5 0 1 1 0-7.071",viewW:110,viewH:110}},{id:"cooktop",group:"appliance",art:{d:"M27.5 29h55a2.5 2.5 0 0 1 2.5 2.5v47a2.5 2.5 0 0 1-2.5 2.5h-55a2.5 2.5 0 0 1-2.5-2.5v-47a2.5 2.5 0 0 1 2.5-2.5 M33.5 66.5a8 8 0 1 0 16 0 8 8 0 1 0-16 0m26.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0m3 23a5 5 0 1 0 10 0 5 5 0 1 0-10 0m-29.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0",viewW:110,viewH:110}},{id:"dining_table",group:"furniture",art:{d:"M83.5 43H78l6.742 30.82A1.5 1.5 0 0 0 86.207 75h.54a1.5 1.5 0 0 0 1.482-1.732zm-56 0H33l-6.742 30.82A1.5 1.5 0 0 1 24.793 75h-.54a1.5 1.5 0 0 1-1.482-1.732zm-2.62-7.406A2.5 2.5 0 0 1 27.21 34h56.58a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044A1 1 0 0 1 87.538 43H23.462a1 1 0 0 1-.932-1.362z M88.5 43h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"dishwasher",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5M43 42h24m-1.5-11a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"dryer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5 M55.994 55.5s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m5.979-11s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m-8.519-11s-.886 1.81-.986 3.143c-.132 1.761 1.066 2.554.986 4.321-.067 1.479-.986 3.536-.986 3.536",viewW:110,viewH:110}},{id:"exercise",group:"other",art:{d:"m60.752 62.03-5.379-2.312 11.066-25.75a1.5 1.5 0 0 1 1.97-.786l2.624 1.127a1.5 1.5 0 0 1 .786 1.97zm-18.648-4.512-4.353 1.189-4.474-16.382a1.5 1.5 0 0 1 1.052-1.842l1.459-.398a1.5 1.5 0 0 1 1.842 1.052z M41.297 38.173a2.215 2.215 0 0 1 0 4.43H29.781a2.215 2.215 0 0 1 0-4.43zm33.219 38.091a2.657 2.657 0 1 1 0 5.315H28.453a2.657 2.657 0 1 1 0-5.315zm8.641-51.366a1.795 1.795 0 0 1 .68 2.41l-3.03 5.59a5 5 0 0 1-4.396 2.618H65.657a1.772 1.772 0 1 1 0-3.544h9.61a3 3 0 0 0 2.598-1.5l2.84-4.917a1.795 1.795 0 0 1 2.452-.657M29.339 65.787c0-6.699 6.457-11.503 12.873-9.579l24.291 7.288a10 10 0 0 1 7.127 9.578v3.19H29.339zm0 15.852h7.086l-1.394 3.486a1.5 1.5 0 0 1-1.393.943h-2.8a1.5 1.5 0 0 1-1.5-1.5zm44.291 0h-7.087l1.395 3.486a1.5 1.5 0 0 0 1.392.943h2.8a1.5 1.5 0 0 0 1.5-1.5z M35.54 66.077a4.872 4.872 0 1 0 9.743 0 4.872 4.872 0 1 0-9.744 0",viewW:110,viewH:110}},{id:"fireplace",group:"other",art:{d:"M26.672 34.425h55.99v39.47h-55.99Z M23 74.894a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v5.802a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zM23 29a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v4.425a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zm15.145 21.44a5 5 0 0 1 5-5h23.043a5 5 0 0 1 5 5v23.454H38.145z M44.57 65.633a2.754 2.754 0 0 1 2.754-2.753H62.01v5.507H47.324a2.754 2.754 0 0 1-2.754-2.754m20.194 5.507a2.754 2.754 0 0 0-2.754-2.753H47.324v5.507H62.01a2.754 2.754 0 0 0 2.754-2.754 M44.57 71.14a2.754 2.754 0 1 0 5.508 0 2.754 2.754 0 1 0-5.508 0m14.688-5.507a2.754 2.754 0 1 0 5.507 0 2.754 2.754 0 1 0-5.507 0",viewW:110,viewH:110}},{id:"fridge",group:"appliance",art:{d:"M35.5 18H75a2.5 2.5 0 0 1 2.5 2.5v64A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5v-64a2.5 2.5 0 0 1 2.5-2.5 M33 46.5h44.5v38A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5zm2.5 44a1 1 0 0 0 1 1h37a1 1 0 0 0 1-1V87h-39zm3-49.5v-9.5m0 30.5v-9.5",viewW:110,viewH:110}},{id:"hood",group:"appliance",art:{d:"M44.308 57h21.384L83 69H27zM44 37.5a2.5 2.5 0 0 1 2.5-2.5h17a2.5 2.5 0 0 1 2.5 2.5V57H44zM80.5 74h-51a2.5 2.5 0 0 1-2.5-2.5V70a1 1 0 0 1 1-1h54a1 1 0 0 1 1 1v1.5a2.5 2.5 0 0 1-2.5 2.5",viewW:110,viewH:110}},{id:"kitchen_cabinet",group:"furniture",art:{d:"M38 88a1 1 0 0 0 1 1h32a1 1 0 0 0 1-1v-3H38zm17-68h16.5a2.5 2.5 0 0 1 2.5 2.5v17a2.5 2.5 0 0 1-2.5 2.5H55zm-19 2.5a2.5 2.5 0 0 1 2.5-2.5H55v22H38.5a2.5 2.5 0 0 1-2.5-2.5zM59 33v5m-8-5v5m4 47h16.5a2.5 2.5 0 0 0 2.5-2.5v-17a2.5 2.5 0 0 0-2.5-2.5H55zm-19-2.5a2.5 2.5 0 0 0 2.5 2.5H55V63H38.5a2.5 2.5 0 0 0-2.5 2.5zM67 67h-5m-14 0h-5",viewW:110,viewH:110}},{id:"kitchen_sink",group:"sanitary",art:{d:"M21.5 84h68a2.5 2.5 0 0 0 2.5-2.5v-35a2.5 2.5 0 0 0-2.5-2.5h-68a2.5 2.5 0 0 0-2.5 2.5v35a2.5 2.5 0 0 0 2.5 2.5 M26 78h26.5a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H26a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m23-24v5m9.5 19H85a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H58.5a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m-6-39.5a1 1 0 0 0-1-1H44a1 1 0 0 0-1 1V44h9.5zM58 33v-2a5 5 0 1 0-10 0v6.5M62 54v5",viewW:110,viewH:110}},{id:"nightstand",group:"furniture",art:{d:"M76.5 73.5h-8l1.674 5.441A1.5 1.5 0 0 0 71.608 80H75a1.5 1.5 0 0 0 1.5-1.5zm-42.5 0h8l-1.674 5.441A1.5 1.5 0 0 1 38.892 80H35.5a1.5 1.5 0 0 1-1.5-1.5z M34.5 30.5h41A2.5 2.5 0 0 1 78 33v40.5a2.5 2.5 0 0 1-2.5 2.5h-41a2.5 2.5 0 0 1-2.5-2.5V33a2.5 2.5 0 0 1 2.5-2.5 M32 39.5h46v18H32Zm18.5 9H60M50.5 66H60",viewW:110,viewH:110}},{id:"oven",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5 M40.5 47h29a2.5 2.5 0 0 1 2.5 2.5v20a2.5 2.5 0 0 1-2.5 2.5h-29a2.5 2.5 0 0 1-2.5-2.5v-20a2.5 2.5 0 0 1 2.5-2.5m25-16a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5M43 42h24",viewW:110,viewH:110}},{id:"plant",group:"other",art:{d:"M45.952 75.849a2.5 2.5 0 0 0 2.485 2.231H63.43a2.5 2.5 0 0 0 2.485-2.231l1.571-14.532H44.381zM60.236 44.78C58.423 36.174 66.805 29.605 77 31.87c0 8.155-8.609 16.763-16.763 12.912m-9.761 5.147c1.517-8.665-7.085-14.943-17.195-12.33.28 8.15 9.178 16.458 17.195 12.33 M55.706 55.88v-2.944m0 0c1.133-5.89 7.09-10.956 12.912-15.404M55.706 52.936c-4.938-4.146-7.982-6.16-14.044-9.061m1.453 12.005h25.636a1 1 0 0 1 1 1v3.437a1 1 0 0 1-1 1H43.115a1 1 0 0 1-1-1v-3.436a1 1 0 0 1 1-1",viewW:110,viewH:110}},{id:"rug",group:"other",art:{d:"M90.27 33.874h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.154h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20M28.12 32h54.025a2.5 2.5 0 0 1 2.5 2.5v40.439a2.5 2.5 0 0 1-2.5 2.5H28.12a2.5 2.5 0 0 1-2.5-2.5V34.5a2.5 2.5 0 0 1 2.5-2.5 M25.62 34.5a2.5 2.5 0 0 1 2.5-2.5h1.717v45.439H28.12a2.5 2.5 0 0 1-2.5-2.5zm59.026 0a2.5 2.5 0 0 0-2.5-2.5H80.43v45.439h1.716a2.5 2.5 0 0 0 2.5-2.5zM37.8 54.485l17.801-8.432 17.801 8.432-17.8 8.432z",viewW:110,viewH:110}},{id:"shelving",group:"furniture",art:{d:"M28 58.5h53V39H28Z M64 58.5h5V47a1 1 0 0 0-1-1h-3a1 1 0 0 0-1 1zM52.5 39h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-12 0h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-5 29.5V71a1 1 0 0 1 1 1H49a1 1 0 0 1 1-1v-2.5a1 1 0 0 1-1-1H36.5a1 1 0 0 1-1 1m33.5-10h7v-14a1 1 0 0 0-1-1h-5a1 1 0 0 0-1 1zM47.5 39h-7V25a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1zm-12 34v4a1 1 0 0 1 1 1h14a1 1 0 0 1 1-1v-4a1 1 0 0 1-1-1h-14a1 1 0 0 1-1 1M28 78h53v7H28Z M28 89.5V23a2.5 2.5 0 0 1 2.5-2.5h48A2.5 2.5 0 0 1 81 23v66.5",viewW:110,viewH:110}},{id:"shower",group:"sanitary",art:{d:"M31 84h49a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H31a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1m31.5-47.6v-4.686a11.7 11.7 0 0 0-3.441-8.283A11.77 11.77 0 0 0 50.75 20a11.77 11.77 0 0 0-8.309 3.431A11.7 11.7 0 0 0 39 31.714V84 M72 39.25a10.25 10.25 0 1 0-20.5 0zm-17.25 5v2.5m7.25-2.5v2.5m7.25-2.5v2.5m-14.5 3.75V53M62 50.5V53m7.25-2.5V53m-14.5 3.75v2.5m7.25-2.5v2.5m7.25-2.5v2.5M54.75 63v2.5M62 63v2.5m7.25-2.5v2.5",viewW:110,viewH:110}},{id:"sink",group:"sanitary",art:{d:"M30 57c0 5.523 4.477 10 10 10h29.5c5.523 0 10-4.477 10-10v-6.5A2.5 2.5 0 0 0 77 48H32.5a2.5 2.5 0 0 0-2.5 2.5zm14 22a1 1 0 0 0 1 1h19.5a1 1 0 0 0 1-1V67H44zm6-36.5a1 1 0 0 1 1-1h7.5a1 1 0 0 1 1 1V48H50zM45 37v-2a5 5 0 1 1 10 0v6.5",viewW:110,viewH:110}},{id:"sofa",group:"furniture",art:{d:"M59.5 29h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5m-34 0h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5 M18.5 44a5 5 0 0 1 5 5v12H86V49a5 5 0 0 1 5-5h.5a5 5 0 0 1 5 5v21.5a5 5 0 0 1-5 5H18a5 5 0 0 1-5-5V49a5 5 0 0 1 5-5z M25.25 57h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 25.25 57m34 0h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 59.25 57M18.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 23.392 82H20a1.5 1.5 0 0 1-1.5-1.5zm72 0h-8l1.674 5.441A1.5 1.5 0 0 0 85.608 82H89a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"stairs",group:"other",art:{d:"M73.47 46.365v-17.62m-10.571 25.55V36.674M52.326 61.783V45.484M42.193 69.273v-15.86M28.979 77.761a1 1 0 0 1 1-1h53.624v6.489a1 1 0 0 1-1 1H29.98a1 1 0 0 1-1-1z M26.777 57.497a1 1 0 0 1 1-1h2.846a1 1 0 0 1 1 1V83.25a1 1 0 0 1-1 1h-2.846a1 1 0 0 1-1-1z m73.113 28.633-41.492 32.93v-4.405l39.9-32.007a.7.7 0 0 1 .428-.151h3.724a1.762 1.762 0 1 1 0 3.524h-2.248a.5.5 0 0 0-.312.109M25.895 53.414a3.084 3.084 0 1 0 6.167 0 3.084 3.084 0 1 0-6.167 0m14.978 16.859a1 1 0 0 1 1-1h41.73v7.488h-42.73zm10.131-7.489a1 1 0 0 1 1-1h31.598v7.488H51.004z M61.576 55.294a1 1 0 0 1 1-1h21.026v7.49H61.576z M72.15 47.806a1 1 0 0 1 1-1h9.454a1 1 0 0 1 1 1v6.489H72.15z",viewW:110,viewH:110}},{id:"toilet",group:"sanitary",art:{d:"M32 26.5a2.5 2.5 0 0 1 2.5-2.5H45a2.5 2.5 0 0 1 2.5 2.5V57H32zM47.5 52h29a2.5 2.5 0 0 1 2.5 2.5V57H47.5zm17.037 20.102C73.95 70.288 79 62.01 79 57H32c.5 7.5 8.072 7 7 11l-2.342 16.862a1 1 0 0 0 .99 1.138H66.72a1 1 0 0 0 .97-1.243z",viewW:110,viewH:110}},{id:"tv",group:"appliance",art:{d:"M23.5 31H86a2.5 2.5 0 0 1 2.5 2.5v34A2.5 2.5 0 0 1 86 70H23.5a2.5 2.5 0 0 1-2.5-2.5v-34a2.5 2.5 0 0 1 2.5-2.5M50 70h9.5v8H50Zm-10 8h30M42.5 45.5h5m5 0h-5m0 0v9.75 m56.75 45.5 5 10 5-10",viewW:110,viewH:110}},{id:"wardrobe",group:"furniture",art:{d:"M28 23a2.5 2.5 0 0 1 2.5-2.5h24V85h-24a2.5 2.5 0 0 1-2.5-2.5zm2 65.5a1 1 0 0 0 1 1h47a1 1 0 0 0 1-1V85H30zM81 23a2.5 2.5 0 0 0-2.5-2.5h-24V85h24a2.5 2.5 0 0 0 2.5-2.5zM50.5 48v9.5m8-9.5v9.5",viewW:110,viewH:110}},{id:"washer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"work_table",group:"furniture",art:{d:"M88.5 47h-20v14h20zm-12 4h4m7.984-8h-66v4h66zm-66 4h4v26.5a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5z M24.866 35.597A2.5 2.5 0 0 1 27.197 34h56.577a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044a1 1 0 0 1-.93 1.362H23.46a1 1 0 0 1-.933-1.361zM88.5 61h-20v12.5A1.5 1.5 0 0 0 70 75h17a1.5 1.5 0 0 0 1.5-1.5zm-12 4h4",viewW:110,viewH:110}}];class vh{constructor(t,e){this.host=t,this.hooks=e}place(t,e="mouse"){const s=this.host._decorImagePalette,i=this.host._curSpaceCfg;if(!s||!i)return;const o=this.hooks.decorSnap(t,e),a=Ot(s.width,s.height),n=Bt(a.w,this.host._cellCm,this.host._gridPitch)/b,r=Bt(a.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,l="di"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),h=this.hooks.geometrySnapshot();i.decor=[...this.host._decorList,{id:l,kind:"image",asset_id:s.asset_id,x:G(o[0]/b-n/2),y:G(o[1]/this.host._decorH-r/2),w:n,h:r,opacity:1}],this.host._decorSel=l,this.host._decorTool="select",this.host._decorImagePalette=null,this.hooks.clearFurniturePreview(),this.hooks.recordGeometry(this.host._t("history.decor_add"),h),this.hooks.saveConfig(),this.host.requestUpdate()}renderPlacementPreview(){const t=this.host._furnPreviewInput,e=this.host._decorImagePalette;if(!t||!e||"decor"!==this.host._mode||"image"!==this.host._decorTool||!this.host._pointerModality.hoverEnabled)return d;const s=this.hooks.decorSnap(t.raw,"mouse"),i=Ot(e.width,e.height),o=Bt(i.w,this.host._cellCm,this.host._gridPitch)/b,a=Bt(i.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,n=this.host._display(e.url);return n?f``:d}renderMissing(t,e,s,i,o,a,n,r,l){const h=i+a/2,c=o+n/2;return f` + ${this.host._t("decor.image_unavailable")} + + + ${this.host._t("decor.image_unavailable")} + `}async uploadFromInput(t,e=!1){const s=t.target,i=s.files?.[0];if(s.value="",!i||this.host._decorAssetBusy)return;let o;try{o=await no(i,2097152)}catch{return void this.host._showToast(this.host._t("backup.error.invalid_image"))}if("reject"!==o.kind)return"guard"===o.kind?(this.hooks.setGuardReplace(e),void(this.host._backdropGuard=o.state)):void await this.upload(i,i.name,e);this.host._showToast(this.host._t("toast.plan_formats"))}async upload(t,e,s){if(!this.host._decorAssetBusy){this.host._decorAssetBusy=!0;try{const i=new FormData;i.append("file",t,e);const o=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/assets/upload",{method:"POST",body:i}):await fetch("/api/houseplan/assets/upload",{method:"POST",body:i,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await o.json().catch(()=>({}));if(!o.ok||a.error||!a.asset){const t={invalid_format:"backup.error.invalid_image",invalid_image:"backup.error.invalid_image",unsupported_image:"backup.error.unsupported_image",too_large:"backdrop.too_large_title",capacity_exceeded:"decor.image_error_capacity"};throw new Error(this.host._t(t[String(a.error)]||"backup.error.io_error"))}const n=Wt({assets:[a.asset]}).values().next().value;if(!n)throw new Error(this.host._t("backup.error.io_error"));this.host._decorAssets=new Map(this.host._decorAssets).set(n.asset_id,n),this.host._decorAssetCatalog=[n,...this.host._decorAssetCatalog.filter(t=>t.asset_id!==n.asset_id)],s&&"image"===this.host._decorShapeDialog?.kind?this.host._decorShapeDialog={...this.host._decorShapeDialog,assetId:n.asset_id}:this.host._decorImagePalette=n,this.host.requestUpdate()}catch(t){this.host._showToast(this.host._t("decor.image_upload_failed",{err:this.host._errText(t)}))}finally{this.host._decorAssetBusy=!1}}}async delete(t){if(t.used_by?.length)return void this.host._showToast(this.host._t("decor.image_in_use"));if(await this.host._confirmDanger({key:`delete-decor-asset:${t.asset_id}`,kind:"destructive",title:this.host._t("decor.image_delete_title"),message:this.host._t("decor.image_delete_message",{name:t.name}),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}))try{await this.host.hass.callWS({type:"houseplan/assets/delete",asset_id:t.asset_id}),this.host._decorAssetCatalog=this.host._decorAssetCatalog.filter(e=>e.asset_id!==t.asset_id),this.host._decorAssets.delete(t.asset_id),this.host._decorImagePalette?.asset_id===t.asset_id&&(this.host._decorImagePalette=null),this.host.requestUpdate()}catch(t){this.host._showToast("in_use"===t?.code?this.host._t("decor.image_in_use"):this.host._t("backup.error.io_error"))}}async loadCatalog(){try{const t=await this.host.hass.callWS({type:"houseplan/assets/list"}),e=Wt(t);this.host._decorAssetCatalog=[...e.values()],this.host._decorAssets=new Map([...this.host._decorAssets,...e]),this.host.requestUpdate()}catch{this.host._showToast(this.host._t("backup.error.io_error"))}}renderImagePalette(){const t=this.host._decorImagePalette;return p``}renderFurniturePalette(){const t=this.host._furnPalette,e=this.host._furnCategory,s=jt.flatMap(t=>Gt(t)),i=bh.filter(t=>s.some(e=>e.category===t.id)),o=i.find(t=>t.id===e)||null,a=o?s.filter(t=>t.category===o.id):[],n=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`
    t.stopPropagation()}> +
    + ${this.host._t("furn.title")} + + +
    +
    + ${o?p` + +
    + ${this.host._t(`furn.cat_${o.id}`)} +
    +
    + ${a.map(e=>p``)} +
    + `:jt.map(t=>p` +
    ${this.host._t(`furn.group_${t}`)}
    +
    + ${i.filter(e=>e.group===t).map(t=>p``)} +
    `)} +
    + ${t?p`
    + + this.host._furnPalette={...t,w:this.hooks.furnFieldToCm(Number(e.target.value))}} /> + + this.host._furnPalette={...t,h:this.hooks.furnFieldToCm(Number(e.target.value))}} /> + ${this.host._t("furn.place_hint")} +
    `:p`
    ${this.host._t("furn.pick_hint")}
    `} +
    `}furnitureSizeInput(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i=qt(e,this.host._imperial,Ut*this.host._cellCm),o="w"===t?"sizeWField":"sizeHField",a="w"===t?"sizeWCm":"sizeHCm",n="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[o]:e,...null===i?{}:{[a]:Math.abs(i),[n]:i<0}}}furnitureFlip(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i="w"===t?s.sizeWField:s.sizeHField,o=qt(i,this.host._imperial,Ut*this.host._cellCm),a="w"===t?s.sizeWCm:s.sizeHCm,n=Math.abs(o??a??.1),r="w"===t?"sizeWField":"sizeHField",l="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[r]:Kt(n,e,this.host._imperial),[l]:e}}renderShapeDialog(){const t=this.host._decorShapeDialog,e="rect"===t.kind||"ellipse"===t.kind,s=this.host._t(`decor.${t.kind}`),i=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m"),o="furniture"===t.kind||"image"===t.kind?qt(t.sizeWField,this.host._imperial,Ut*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?qt(t.sizeHField,this.host._imperial,Ut*this.host._cellCm):null,n=!("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a),r="image"===t.kind?this.host._decorAssetCatalog.find(e=>e.asset_id===t.assetId)||this.host._decorAssets.get(t.assetId||""):null;return p`this.host._decorShapeDialog=null}> +
    + ${"furniture"===t.kind?p` + + `:"image"===t.kind?p` + + + ${r?p`
    + ${r.name} + ${r.name} +
    `:d} + `:d} + ${"image"===t.kind?p` + + this.host._decorShapeDialog={...t,opacity:Number(e.target.value)}} />`:p`this.host._decorShapeDialog={...t,...e.detail}}>`} + ${"image"!==t.kind?p` +
    this.host._decorShapeDialog={...t,widthCm:this.host._decorSmallCm(Number(e.target.value))}} />${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    `:d} + ${"line"===t.kind?p` + +
    + + +
    + +
    this.host._decorShapeDialog={...t,lengthCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ${i}
    `:"furniture"===t.kind||"image"===t.kind?p` + +
    this.furnitureSizeInput("w",t.target.value)} /> + ×this.furnitureSizeInput("h",t.target.value)} /> + ${i}
    + + `:p` + +
    this.host._decorShapeDialog={...t,sizeWCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ×this.host._decorShapeDialog={...t,sizeHCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ${i}
    `} + + this.host._decorShapeDialog={...t,angle:e.target.value}} /> + ${e?p` + this.host._decorShapeDialog={...t,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}>`:d} +
    +
    + + + +
    +
    `}}const yh=(t,e)=>t[0]*e[1]-t[1]*e[0],$h=(t,e)=>t[0]*e[0]+t[1]*e[1],wh=(t,e)=>[t[0]-e[0],t[1]-e[1]],kh=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function xh(t,e,s){return Math.abs(yh(t,e))<=s}function Sh(t,e,s){return Math.abs(yh(wh(t,e),s))}function Mh(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const l=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&xh(l,[i/n,a/n],1e-6)))return[];if(Sh(e.a,t,l)>1.1*Math.max(s,o)||Sh(e.b,t,l)>1.1*Math.max(s,o))return[];return[[$h(wh(e.a,t),l),$h(wh(e.b,t),l)]]}),o).flatMap(([e,i])=>{const o=Yt({a:kh(t,l,e),b:kh(t,l,i),halfDepth:s});return o?[o]:[]})}function Dh(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=K(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=Vt(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:kh([t.x,t.y],s,-i),b:kh([t.x,t.y],s,i)}}),a=[];for(const i of Jt(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Mh(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=x(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Mh(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function Ch(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=kh(s.center,a,o),r=[];for(let e=0;ei&&xh(s.axis,[l[0]/h,l[1]/h],1e-6)))continue;if(Sh(o,n,s.axis)>i||Sh(a,n,s.axis)>i)continue;const c=$h(wh(o,n),s.axis),d=$h(wh(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const l=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!l)return null;const[h,c]=l,d=kh(n,s.axis,-s.half),p=kh(n,s.axis,s.half),u=Math.max(0,-s.half-h),_=Math.max(0,c-s.half),m=u>0?kh(n,s.axis,h):d,g=_>0?kh(n,s.axis,c):p,f=$h(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,u),right:b(p,g,_)}}function Ph(t){const e=kh(t.center,t.axis,-t.half),s=kh(t.center,t.axis,t.half),i=kh(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=kh(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Ih(t,e,s,i){const o=wh(s,e),a=$h(o,o);if(!(a>i*i))return Math.hypot(...wh(t,e))<=i;const n=$h(wh(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=kh(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...wh(t,r))<=i}function Th(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=h>t[1]&&t[0]<(l-n)*(t[1]-r)/(h-r)+n&&(i=!i)}return i}function zh(t,e,s,i,o,a){const n=wh(i,s),r=yh(e,n);if(Math.abs(r)<=a)return null;const l=wh(s,t),h=yh(l,n)/r,c=yh(l,e)/r;return h<-a||h>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,h))}function Rh(t,e,s){const i=Ph(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Th(t,r.body,a))return 0;for(let i=0;i$h(wh(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=wh(n,a),l=Math.hypot(r[0],r[1]);if(!(l>s&&xh(e.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(Sh(a,e.center,e.axis)>s||Sh(n,e.center,e.axis)>s)continue;const h=$h(wh(a,e.center),e.axis),c=$h(wh(n,e.center),e.axis),d=Math.min(h,c),p=Math.max(h,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||u2)return Ph(s);if(i.length){const t=i.map(({room:t,edge:i})=>Ch(t,i,s,e.epsilon));if(t.some(t=>!t))return Ph(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Rh(t,e,s)}const Fh="1.72.0-beta.2",Nh={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Hh={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},Lh=2e3,Eh=1e3,Oh=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},Bh=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},Wh=1e3,jh=2e3,Gh=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},qh="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";class Uh{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._resizePreviewNodes=null,this._resizeBaselineLimits=[],this._resizeBaseFrameStable=!0,this._supportPreviewGeneration=0,this._decorAssetGuardReplace=null,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=Oh(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Xt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?Xt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Oh(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&Zt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&Qt(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=te(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=Oh(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const l=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,l||this.host.requestUpdate()},this._deletePhysicalSelection=async()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void await this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=async()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._space;if(!t||!this.host._curSpaceCfg)return;if(!await this.host._confirmDanger({key:"delete-draft",kind:"destructive",title:this.host._t("confirm.delete_draft_title"),message:this.host._t("confirm.delete_draft_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")})||this.host._space!==e)return;const s=this.host._curSpaceCfg;if(!s||!(s.room_drafts||[]).some(e=>e.id===t))return;const i=this._geometrySnapshot();s.room_drafts=(s.room_drafts||[]).filter(e=>e.id!==t),s.room_drafts.length||delete s.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),i)},this._deleteDraftSegment=async()=>{const t=this.host._physicalDialog,e=this.host._space;if(!t||"draft"!==t.kind||!this.host._curSpaceCfg)return;const s=t.id,i=t.segment||0,o=await this.host._confirmDanger({key:"delete-draft-segment",kind:"destructive",title:this.host._t("confirm.delete_draft_segment_title"),message:this.host._t("confirm.delete_draft_segment_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._physicalDialog;if(!o||this.host._space!==e||"draft"!==a?.kind||a.id!==s||(a.segment||0)!==i)return;const n=this.host._curSpaceCfg;if(!n)return;const r=(n.room_drafts||[]).findIndex(t=>t.id===s);if(r<0)return;const l=n.room_drafts[r];if(!Array.isArray(l.segments)||!Number.isInteger(i)||i<0||i>=l.segments.length)return;const h=i,c=[],d=l.points.slice(0,h+1),p=l.points.slice(h+1);if(d.length>=2&&c.push({id:l.id,points:d,segments:l.segments.slice(0,h)}),p.length>=2&&c.push({id:c.length?`${l.id}-${Date.now().toString(36)}`:l.id,points:p,segments:l.segments.slice(h+1)}),2===c.length&&n.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const u=this._geometrySnapshot();n.room_drafts.splice(r,1,...c),n.room_drafts.length||delete n.room_drafts,this.host._activeDraftId===l.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=c.length?{kind:"draft",id:c[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),u)},this._furnShiftListener=t=>{if("Shift"!==t.key)return;const e=this.host._furnPreviewInput;if(!e)return;const s="keydown"===t.type;e.free!==s&&(this.host._furnPreviewInput={...e,free:s},this.host.requestUpdate())},this._furnShiftAttached=!1,this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=Tr(lt(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>jh)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot(),h=ee(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*Wh,Number(e.a?.[1])*Wh],i=[Number(e.b?.[0])*Wh,Number(e.b?.[1])*Wh];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/Wh,a.interval.a[1]/Wh],b:[a.interval.b[0]/Wh,a.interval.b[1]/Wh],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*Wh,i.a[1]*Wh],b:[i.b[0]*Wh,i.b[1]*Wh]};e.host={kind:"partition",id:i.id,t:zr([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(h,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>jh)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=or(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,ht);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:re(this.host._settings,{bgColor:null})||null,northDeg:ne(this.host._settings,{}),bgMode:ae(this.host._settings,{}),sunRays:oe(this.host._settings,{}),showRoomTooltip:ie(this.host._settings),zigbeeTopology:se(this.host._settings),busy:!1}},this._openSupportDialog=()=>{this.host._norm&&this.host._canEdit&&(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPreviewGeneration+=1,this.host._supportDialog={draftId:dh("draft"),idempotencyKey:dh("report"),submissionFingerprint:"",contact:"",message:"",attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:"",reportId:""})},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._decorStylePersistTimer=null,this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:le).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},this._discoveryPreviewMemo=null,this._decorImages=new vh(t,{decorSnap:(t,e)=>this._decorSnap(t,e),geometrySnapshot:()=>this._geometrySnapshot(),clearFurniturePreview:()=>this._clearFurniturePreview(),recordGeometry:(t,e)=>this._recordGeometry(t,e),saveConfig:()=>this._saveConfig(),saveShape:()=>this._decorSaveShape(),setGuardReplace:t=>{this._decorAssetGuardReplace=t},furnShiftDetach:()=>this._furnShiftDetach(),furnPick:t=>this._furnPick(t),furnFieldValue:t=>this._furnFieldValue(t),furnFieldToCm:t=>this._furnFieldToCm(t)}),t._editorSecondary=new Br({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Xa}_routeLiveEditorUpdate(t,e){const s=Bo(this.host,t,e);return!s&&this.host._resize.preview&&(this._resizeBaseFrameStable=!1),s}_commitLiveEditor(){!function(t){const e=t,s=Eo(t);s.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(s.raf),s.raf=0,Go(s);const i=e.renderRoot?.querySelector("[data-hp-live-editor]");i&&g(d,i);const o=e.renderRoot?.querySelector("[data-hp-live-editor-html]");if(o){const t=e._viewOr(e._baseVb());g(e._renderLiveEditorMeasurements(t),o)}}(this.host)}_disposeLiveEditor(){!function(t){Ao(t);const e=Fo.get(t);e&&(e.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(e.raf),Go(e),Fo.delete(t))}(this.host)}_queuePointerMove(t,e){zo(this.host,t,e)}_flushPointerMove(t){Ro(this.host,t)}_cancelPointerMove(t){Ao(this.host,t)}_help(t){const e=`${t}.aria`,s=l(this.host.hass,this.host._config?.language);return he(s,t)&&he(s,e)?p``:d}_setMode(t,e=!0){t!==this.host._mode&&this.host._cancelDangerConfirm(),this.host._endTabDrag(),this._clearFurniturePreview(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if("devices"===this.host._mode&&this.host._cancelDeviceDrag(),this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const l=!s.bg&&"view"===t!=("view"===i),h=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=h?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=h?h.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=h?h.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}l&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?ce([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const u=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(u,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&u===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=de(ht,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:de(e,this.host._imperial),max:de(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=il(s,{pitch:W,seedIds:e,geometry:al(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,ol(t.openings,t.partitions,i.openingMoves,{coordScale:Wh,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=ar(this.host._path,or(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,ht)),s=e.length;if((t.partitions||[]).length+s>jh)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=jh:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[pe(ue(t[0],e)),pe(ue(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?_e(s,t,this.host._gridPitch,so):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=gt(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=Yn({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=Jn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=Yn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?sr(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(Bn(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??On,o=s.excludePoints||[],a=tr(Qn(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=st([e[0],e[1]],t);if(r>s.tolerance)continue;const l=er(a,e,s.gridStep);!Xn(l,o,i)&&Zn(r,a.key,n)&&(n={kind:"line",point:l,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,l=r?[...r.point]:this._snapDrawPoint(t,e),h=i?me(i,l):l;return{point:h,candidate:r&&ft(h,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return ft(t,e)}_dropLegacySegments(t=this.host._serverCfg){for(const e of t?.markers||[])"ripple"===e.display&&(e.display="icon_ripple");for(const e of t?.spaces||[]){delete e.segments;const t=new Set,s=e=>"string"==typeof e&&e.length>=1&&e.length<=64&&!t.has(e),i=e=>(t.add(e),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Ut);if(Array.isArray(e.partitions)&&(e.partitions=e.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),e.partitions.length||delete e.partitions),Array.isArray(e.wall_columns)&&(e.wall_columns=e.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:te(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:ge(t.angle)}})),e.wall_columns.length||delete e.wall_columns),Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=Al(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),e.room_drafts.length||delete e.room_drafts),Array.isArray(e.walls)){const t=fe(Et({spaces:[e]}),e.id),s=t?be(e,t,Wh,.02*Tt).contour.map(t=>t.map(t=>t/Wh)):yt(e.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);e.walls=zt(e.walls,e.rooms||[],W,1,s),e.walls.length||delete e.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=_(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_prepareConfigCandidate(t){return this._dropLegacySegments(t),ve(t)}_writeConfig(t=null){this.host._writesPending++,this.host._writeChain=ye(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;const e=_(this.host._serverCfg),s=this._prepareConfigCandidate(this.host._serverCfg),i=_(s);t&&e===t.attemptedFingerprint&&Object.assign(t,{revision:this.host._cfgRev,attempted:s,attemptedFingerprint:i});const o=[...this.host._pendingPhysicalWrites.entries()];for(const[t,e]of o){const i=s.spaces.find(e=>e.id===t),o=$e(i);if(o===e.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(s,t).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(e.before),this.host._pendingPhysicalWrites.delete(t),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});e.fingerprint=o}i!==_(this.host._serverCfg)&&(this.host._serverCfg=s),this.host._cfgContentFingerprint=i;try{await this.host._sendConfigCandidate(s)}catch(t){if(!this._rollbackRejectedPhysicalWrites(o))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of o)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),l=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=l.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof Ml?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);return o?function(t,e,s,i,o){const a=(t?.spaces||[]).find(t=>t?.id===e);if(!a)return[];const n=Number(a.cell_cm)>0?Number(a.cell_cm):5,r=[...Sn(s),...In(s,n,W),...zn(s,n,W)];let l=i?.multiWallNodes||null;if(!l)try{l=J(a.rooms||[],a.walls||[],[],W,n,W,1)}catch{l=null}const h=i&&"lightweight"!==i.status?i:null;let c="ok"===h?.status||"degraded-extra"===h?.status?h.roomGeom:null;const d=null===i||"lightweight"===i?.status;if(!c&&!d&&l?.nodes.length)try{const t=Y(a.rooms||[],a.walls||[],[],[],W,n,W,1);c="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{c=null}for(const t of a.rooms||[]){const e=String(t?.id||"");if(!e||o&&!o.has(e))continue;let s=null;try{s=X(a.rooms||[],e,a.walls||[],[],W,n,W,1,d?null:c??void 0,l)}catch{s=null}r.push(...Rn(e,s,n,W))}return r}(t,e,this._limitSegmentsOf(o),s,i):[]}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i,o){const a=o?.length?new Set(o):void 0;let n;const r=(e.spaces||[]).find(t=>t?.id===s);let l="";try{l=$e(r)}catch{l=""}const h=!a&&e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(h&&l&&h.fingerprint===l&&h.spaceId===s&&(n=h.violations),!n){try{const t=Number(e?.model_version||0)>=9?e:Xl(e).config;n=this.host._junctionLimitViolations(t,s,a?null:void 0,a)}catch{return[]}!a&&e&&"object"==typeof e&&l&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:l,violations:n})}let c=[],d=n;try{c=this.host._junctionLimitViolations(t,s,i,a),a&&(d=n.filter(t=>"clearance"!==t.rule||a.has(t.subject)))}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return An(c,d)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||$e(e)===$e(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let l=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new Ml("invalid-room",e.spaceId);const s=Xl(t).config;l=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new Ml("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),h=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of h?.wall_segments||[])t.cm=St(o.walls,t.a,t.b,W,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=Ul(s,Gl(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=at(e),l=(s.rooms||[]).find(e=>e.id===t.id),h=at(l),c=at(n);if(r&&h&&c&&r.length===h.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;aSl||Hl(r,s,i)>Sl)continue;const l=Nl(n,s,i),h=Nl(r,s,i);l<-Sl||h>1+Sl||h<=l+Sl||o.push({start:l,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,h[e],h[(e+1)%h.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/Wh,t[1]/Wh]):[];i.push(...s.map(t=>[t[0],t[1]])),h=Fl(t)<=Fl(l,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{h=!1}if(!h)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;Ql(i,n),this._recordGeometry(t,l);const p=i.spaces.find(t=>t.id===e.spaceId),u=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:u?.before||l,fingerprint:$e(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this._clearFurniturePreview(),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){return clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._furnTouchPending?.pid===t.pointerId?(this._clearFurniturePreview(),this.host._pointers.delete(t.pointerId),void this.host.requestUpdate()):this.host._physicalDrag?.pid!==t.pointerId&&this.host._physicalRotate?.pid!==t.pointerId?this.host._decorDraft?.pid===t.pointerId?(this.host._decorDraft=null,void this.host.requestUpdate()):void(this.host._decorMove?.pid!==t.pointerId&&this.host._dtDrag?.pid!==t.pointerId&&this.host._bdDrag?.pid!==t.pointerId?("opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover(),this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),this.host._viewportGestureDirty&&0===this.host._pointers.size&&(this.host._viewportGestureDirty=!1,this.host.requestUpdate())):this._cancelDecorGesture()):void this._cancelPhysicalGesture()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=$e(s)!==$e(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=Xl(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;Ql(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:$e(n)})}else Ql(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=at(e);return!!s&&we(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=at(e);return!!s&&E(t,s)})}_pointInRoom(t,e){return e.poly?ke(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!xa(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>jh)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=me(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this.host._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const h=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(h)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),l=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),h=this.host._path[this.host._path.length-1],c=this._samePt(h,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),u=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],_=[...p,...null==d?[]:[{cm:d}],...l],m=u.length>=4&&this._samePt(u[0],u[u.length-1]),g=u,f=_;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||B(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/Wh,t[1]/Wh]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=or(i,e,this.host._drawWallCm,ht);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*Wh,Number(t?.[1])*Wh]):[],o=Array.isArray(e?.segments)?e.segments:[];return or(Math.max(0,t.length-1),s,this.host._drawWallCm,ht).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/Wh,t[1]/Wh]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=Dr(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=Er(r,{requiredSourceKey:o,maxDistance:x(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=at(t);if(!t.id||!e)continue;const s=xe(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=B(i)>=B(o)?i:o,r=a===i?o:i,l=[...n].sort((t,e)=>Math.abs(t.area-B(r))-Math.abs(e.area-B(r))||t.key.localeCompare(e.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const l=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=Cr(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=Er(this._wallGraphSources([]),{point:t,maxDistance:x(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=Er(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(Xt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:te(s),angle:0},a=(i.wall_columns||[]).find(t=>Zt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/Wh,e[1]/Wh]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:de(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:de(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?ge(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:de(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),Gh(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())Se(o,Me(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=De(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&Se(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),l=this.host._physicalPickCycle,h=a.length>1&&l?.signature===n&&r-l.at>380&&r-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:h,x:t.clientX,y:t.clientY,at:r},e=a[h].kind,s=a[h].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Ut*Wh-Math.min(...o),Math.min(Ut*Wh-Math.max(...o),s[0])),Math.max(-Ut*Wh-Math.min(...a),Math.min(Ut*Wh-Math.max(...a),s[1]))]}_physicalMove(t){t.stopPropagation(),zo(this.host,"physical",()=>this._physicalMoveNow(t))}_physicalMoveNow(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){Ro(this.host,"physical");const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/Wh,o.a[1]/Wh],t.b=[o.b[0]/Wh,o.b[1]/Wh];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=nt(t,[o],Wh,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,rt(t,s,Wh))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&Zt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/Wh,(o.center[1]+e.delta[1])/Wh])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){Ao(this.host,"physical"),Ao(this.host,"physical-rotate"),this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),Gh(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:ge(e.angle),angle:ge(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){t.preventDefault(),t.stopPropagation(),zo(this.host,"physical-rotate",()=>this._physicalRotateMoveNow(t))}_physicalRotateMoveNow(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=ge(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){Ro(this.host,"physical-rotate");const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&Zt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?at(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:x(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*Wh,t.a[1]*this.host._spaceH],b:[t.b[0]*Wh,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?x(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!xa(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],l=n.poly[(i+1)%n.poly.length],h=Ca(r,l,a);if(!h)return{enabled:!1,reason:"diagonal"};if(!za(n.poly,i,h,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(Fa(t,r,l,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=ya(fa(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!xa(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!za(t.room.poly,t.edge,h,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(wa([t.x,t.y],r,l)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=La(t,e,o,1,a),l=La(t,e,n,0,a);if(!r||!l)return{enabled:!1,reason:"partial-shared"};v.push(r,l)}const y={roomId:s,edge:i,a:[...r],b:[...l],n:ka(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!qa(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>qa(t,e,y,s,o))){if(!s.some(s=>Ba(Wa(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>qa(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>qa(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const l=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/Wh,t[1]/l]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/Wh,s.y=e[1]/l)}const h=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*Wh,t[1]*l]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/Wh,i[t][1]/Wh],o.b=[i[(t+1)%i.length][0]/Wh,i[(t+1)%i.length][1]/Wh])}}if(h.length&&Array.isArray(r.walls)&&r.walls.length){const t=Ce(r.walls,h,c,this.host._wallKeyPitch,Wh,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=at(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),u=new Map;for(const t of n.walls||[]){const e=p(t);u.set(e,(u.get(e)||0)+1)}const _=[];for(const t of r.walls||[]){const e=p(t),s=u.get(e)||0;s?u.set(e,s-1):_.push(t)}if(Pe(_,d,this.host._wallKeyPitch,Wh,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=function(t,e,s=1e-8){const i=Qa(t),o=new Set(e),a=i.flatMap(t=>{if(!o.has(String(t.id||"")))return[];const e=on(sn(t));return e?[e]:[]});return i.flatMap(t=>{const e=String(t.id||""),i=on(sn(t)),n=!!i&&a.some(t=>i.minX<=t.maxX+s&&i.maxX+s>=t.minX&&i.minY<=t.maxY+s&&i.maxY+s>=t.minY);return e&&(o.has(e)||n)?[e]:[]})}(r.rooms||[],i),g=an(r,m);if(!g||!this._rszSpaceCandidateGeometry(this.host._space,g).ok)return{ok:!1,reason:"physical-geometry"};const f={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)};try{this._resizePreviewNodes=J(g.rooms,g.walls,[],W,this.host._cellCm,this.host._gridPitch,Wh)}catch{this._resizePreviewNodes=null}let b;try{const t=new Set(i),e=this.host._junctionLimitViolations(f,this.host._space,{status:"lightweight",multiWallNodes:this._resizePreviewNodes},t);b=An(e,this._resizeBaselineLimits.filter(e=>"clearance"!==e.rule||t.has(e.subject)))}catch{b=[{rule:"check_failed",subject:this.host._space,actual:0,limit:0}]}return b.length?(this.host._rszLimitViolation=b[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:null}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,this.host._physicalBodiesCache&&(this.host._physicalBodiesCache.key=`${this.host._space}|${this.host._cfgEpoch}|${this.host._cellCm}|${this.host._gridPitch}`),!t||!e)return;const s=Ie(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:_([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};Bh(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;this._resizeBaseFrameStable=!0,t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));Gh(t);const a=o.plan;try{this._resizeBaselineLimits=this.host._junctionLimitViolations(this.host._serverCfg,this.host._space,null,new Set(a.roomIds))}catch{this._resizeBaselineLimits=[]}const n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,l=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,h=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:h,before:JSON.parse(h),wallUnionBefore:l,epochBefore:this.host._cfgEpoch})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),zo(this.host,"resize",()=>this._rszMoveNow(t)))}_rszMoveNow(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(Ro(this.host,"resize"),!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){Ao(this.host,"resize");const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(null!==e.restoreEpoch?this.host._cfgEpoch=e.restoreEpoch:this.host._cfgEpoch++,this.host._physicalBodiesCache&&(this.host._physicalBodiesCache.key=`${this.host._space}|${this.host._cfgEpoch}|${this.host._cellCm}|${this.host._gridPitch}`),e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};Bh(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}null!==e.restoreEpoch&&"plan"===this.host._mode&&this._resizeBaseFrameStable&&(this.host._terminalFrame=1),this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):Te(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const l="mi"===this.host.hass?.config?.unit_system?.length,h=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),u=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this.host._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?ze(b>8?2.5:b,f,u.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of h){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&X(f.rooms,s,c,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh,null,this._resizePreviewNodes)||a,r=d.length?Re(Ae(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:Fe(n,this.host._gridPitch,this.host._cellCm),h=Ne(r,l),p=rn({poly:a,edge:e.edgeByRoom[s],text:h,view:g,gearCenter:He(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:h,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=Le(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>Ee(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return d;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return f``}_renderResizeLayer(t,e){const s=Math.max(.013*t.w,5),i=s/2,o=t=>t.toFixed(1),a=`M ${o(-.7*i)} 0 H ${o(.7*i)} M 0 ${o(-.22*i)} V ${o(-i)} M ${o(-.32*i)} ${o(-.6*i)} L 0 ${o(-i)} L ${o(.32*i)} ${o(-.6*i)} M 0 ${o(.22*i)} V ${o(i)} M ${o(-.32*i)} ${o(.6*i)} L 0 ${o(i)} L ${o(.32*i)} ${o(.6*i)}`,n=[],r=this._rszRooms(),l=this.host._resize.snapshotIdentity||this._rszSnapshot(),h=e?.length?new Set(e):null;for(const t of r)if(!h||h.has(t.id))for(let e=0;ethis._rszEdgeDown(s,t.id,e)} + @pointermove=${t=>this._rszMove(t)} + @pointerup=${t=>this._rszUp(t)} + @pointercancel=${t=>this._rszPointerCancel(t)} + @lostpointercapture=${t=>this._rszPointerCancel(t)} + @click=${u?t=>this._rszDisabledActivate(t,p.reason):null} + @keydown=${u?t=>this._rszDisabledKey(t,p.reason):null}> + ${_} + `),n.push(f``)}return f`${n}`}_partitionOpeningCuts(t=this.host._spaceModel(),e=()=>!0){if(!t)return[];const s=this.host._curSpaceCfg?.id===t.id?this.host._curSpaceCfg:null,i=Oe(s,t,this.host._cellCm,this.host._gridPitch,Wh);return Be(i,e)}_decorSnapGeometry(t){const e=this.host._spaceModel();if(!e)return{points:[],segments:[]};const s=t||"",i=this.host._decorSnapCache;if(i&&i.epoch===this.host._cfgEpoch&&i.space===this.host._space&&i.height===this.host._decorH&&i.exclude===s)return i.geometry;const o=[];for(const e of this.host._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*Wh,e.y1*this.host._decorH],s=[e.x2*Wh,e.y2*this.host._decorH];o.push({points:[t,s,[(t[0]+s[0])/2,(t[1]+s[1])/2]],segments:[{a:t,b:s}]})}else if("text"===e.kind)o.push({points:[[e.x*Wh,e.y*this.host._decorH]],segments:[]});else{const t=this.host._decorBoxOf(e);t&&o.push(We(t))}for(const t of e.rooms){const e=at(t);e?.length&&o.push({points:e.flatMap((t,s)=>{const i=e[(s+1)%e.length];return[t,[(t[0]+i[0])/2,(t[1]+i[1])/2]]}),segments:e.map((t,s)=>({a:t,b:e[(s+1)%e.length]}))})}const a=je(o);return this.host._decorSnapCache={epoch:this.host._cfgEpoch,space:this.host._space,height:this.host._decorH,exclude:s,geometry:a},a}_decorSnap(t,e="mouse",s){const i=this.host._stageEl,o=this.host._viewOr(this.host._baseVb()),a="touch"===e||"pen"===e?14:8,n=i?o.w/Math.max(1,i.clientWidth)*a:this.host._gridPitch;return Ge(t,this._decorSnapGeometry(s),n,t=>this._snap(t)).point}_replaceDecor(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>s.id===t?{...s,...e}:s),this.host.requestUpdate())}_cancelDecorGesture(){Ao(this.host,"decor-transform"),Ao(this.host,"decor-move"),Ao(this.host,"backdrop");const t=this.host._decorMove?.before||this.host._dtDrag?.before||this.host._bdDrag?.before,e=t&&this.host._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const s=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=s(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,s(t.plan_transform||{})),this.host._decorSnapCache=null}this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null,t&&e&&"decor"===this.host._mode&&(this.host._terminalFrame=1),this.host.requestUpdate()}_decorPointerDown(t){const e=this.host._decorTool;if("mouse"===t.pointerType&&0!==t.button&&"select"!==e&&"erase"!==e)return!1;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const s=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorDraft={kind:e,a:s,b:s,pid:t.pointerId},Gh(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorTextDialog={x:G(e[0]/Wh),y:G(e[1]/this.host._decorH),text:"",color:this.host._decorStyle.color,opacity:this.host._decorStyle.opacity,angle:"0",sizeCm:qe(Ft,this.host._cellCm,this.host._gridPitch)},this.host._decorTextSelection={start:0,end:0},!0}if("furniture"===e||"image"===e){if("furniture"===e?!this.host._furnPalette:!this.host._decorImagePalette)return!1;t.preventDefault();const s=t.pointerType||"mouse";if("mouse"===s)return"furniture"===e?this._furnPlace(this._svgPoint(t),t.shiftKey,s):this._decorImagePlace(this._svgPoint(t),s),!0;const i=this.host._furnTouchPending;return i&&i.pid!==t.pointerId?(i.cancelled=!0,this.host._furnPreviewInput=null,!0):(this.host._furnTouchPending={pid:t.pointerId,sx:t.clientX,sy:t.clientY,pointerType:s,cancelled:!1},this.host._furnPreviewInput=null,Gh(t),!0)}if("select"===e&&(this.host._decorSel=null),this.host._bdMovable){const e=this.host._bdRect,s=this._svgPoint(t),i=e.x+e.w/2,o=e.y+e.h/2,a=-Ue(e.angle)*Math.PI/180,n=s[0]-i,r=s[1]-o,l=[i+n*Math.cos(a)-r*Math.sin(a),o+n*Math.sin(a)+r*Math.cos(a)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this.host._decorDraft;if(this.host._decorDraft=null,!t)return;const e=.5*this.host._gridPitch;if(!Ke(t.kind,t.a,t.b,e))return;const s=Wh,i=this.host._decorH,o=this.host._decorStyle,a=this._geometrySnapshot(),n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r=G;let l;if("line"===t.kind)l={id:n,kind:"line",x1:r(t.a[0]/s),y1:r(t.a[1]/i),x2:r(t.b[0]/s),y2:r(t.b[1]/i),...Ve(o,!1)};else{const e=r(Math.min(t.a[0],t.b[0])/s),a=r(Math.min(t.a[1],t.b[1])/i),h=Math.abs(t.b[0]-t.a[0])/s,c=Math.abs(t.b[1]-t.a[1])/i;l={id:n,kind:t.kind,x:e,y:a,w:h,h:c,...Ve(o,!0)}}this.host._curSpaceCfg.decor=[...this.host._decorList,l],this.host._decorSel=n,this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this.host._mode)return;const s=this.host._decorTool;if("text"===s){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==s&&"erase"!==s||(t.stopPropagation(),t.preventDefault(),"erase"!==s?(this.host._decorSel=e.id,this.host._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},Gh(t)):this.host._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this.host._decorMove;if(!e)return;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const s=this._svgPoint(t),i=e.orig,o=("line"===i.kind?i.x1:i.x)*Wh,a=("line"===i.kind?i.y1:i.y)*this.host._decorH,n=this._decorSnap([o+(s[0]-e.start[0]),a+(s[1]-e.start[1])],t.pointerType,e.id);let r=(n[0]-o)/Wh,l=(n[1]-a)/this.host._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=Ut;r=Math.max(-_-c,Math.min(_-c-p,r)),l=Math.max(-_-d,Math.min(_-d-u,l)),(r||l)&&(e.moved=!0);this.host._curSpaceCfg.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s=e.orig;return"line"===t.kind?{...t,x1:s.x1+r,y1:s.y1+l,x2:s.x2+r,y2:s.y2+l}:{...t,x:s.x+r,y:s.y+l}}),this.host.requestUpdate()}_dmUp(){Ro(this.host,"decor-move");const t=this.host._decorMove;t?.moved&&(this._recordGeometry(this.host._t("history.decor_move"),t.before),this._saveConfig()),this.host._decorMove=null,this.host.requestUpdate()}_decorShapeDbl(t,e){"decor"===this.host._mode&&"select"===this.host._decorTool&&(t.preventDefault(),t.stopPropagation(),this.host._decorMove=null,this.host._decorSel=e.id,this._openDecorProperties(e))}_openDecorProperties(t){if("decor"!==this.host._mode||"select"!==this.host._decorTool)return;if("text"===t.kind)return void this._decorOpenText(t);if(!["line","rect","ellipse","furniture","image"].includes(t.kind))return;"image"===t.kind&&this._decorImageCatalogLoad();const e=this.host._decorResolvedStyle(t),s="line"===t.kind?t:null,i=this.host._decorBoxOf(t);this.host._decorShapeDialog={id:t.id,kind:t.kind,color:e.color,opacity:e.opacity,widthCm:e.widthCm,angle:this.host._angleField(Ue(s?Je([s.x1*Wh,s.y1*this.host._decorH],[s.x2*Wh,s.y2*this.host._decorH]):t.angle)),...s?{lengthCm:qe(Math.hypot((s.x2-s.x1)*Wh,(s.y2-s.y1)*this.host._decorH),this.host._cellCm,this.host._gridPitch),lineStyle:"dashed"===s.line_style?"dashed":"solid"}:{},...i?{sizeWCm:qe(i.w,this.host._cellCm,this.host._gridPitch),sizeHCm:qe(i.h,this.host._cellCm,this.host._gridPitch)}:{},..."furniture"===t.kind?{symbol:t.symbol,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Kt(qe(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Kt(qe(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."image"===t.kind?{assetId:t.asset_id,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Kt(qe(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Kt(qe(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."rect"===t.kind||"ellipse"===t.kind?{fill:e.fill,fillColor:e.fillColor,fillOpacity:e.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const s=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Ht(t[1])),i=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||i?"":Lt(t.entity,o),n=!(!String(t.entity??"").trim()||s||a&&!i);if(a&&!n){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+a+e.slice(t+2):`${e}${e?" ":""}${a}`}this.host._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:D(t.color,this.host._decorStyle.color),opacity:Ye(t.opacity,this.host._decorStyle.opacity),angle:this.host._angleField(t.angle),sizeCm:this.host._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:n||void 0},this.host._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this.host._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this.host._decorTextDialog;if(!e)return;const s=Lt(e.pickerEntity,t);if(!s)return;const i=e.text,o=Math.max(0,Math.min(i.length,this.host._decorTextSelection.start)),a=Math.max(o,Math.min(i.length,this.host._decorTextSelection.end));if(i.length-(a-o)+s.length>200)return;const n=i.slice(0,o)+s+i.slice(a),r=o+s.length;this.host._decorTextSelection={start:r,end:r},this.host._decorTextDialog={...e,text:n,preserveLegacy:void 0},this.host.updateComplete.then(()=>{const t=this.host.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(r,r))})}_decorSaveText(){const t=this.host._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this.host._decorTextDialog=null);const s=this._geometrySnapshot(),i=this.host._curSpaceCfg,o={color:t.color,opacity:Ye(t.opacity),size_cm:Number(Math.max(.1,Math.min(Lh,t.sizeCm)).toFixed(4)),...Ue(t.angle)?{angle:Ue(t.angle)}:{}};if(t.id)i.decor=this.host._decorList.map(s=>{if(s.id!==t.id)return s;if("text"!==s.kind)return s;if(t.preserveLegacy){const{angle:t,size:i,scale:a,...n}=s;return{...n,text:e,...o}}const{entity:i,attr:a,unit:n,...r}=s,{angle:l,size:h,scale:c,...d}=r;return{...d,text:e,...o}});else{const s="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this.host._decorList,{id:s,kind:"text",x:t.x,y:t.y,text:e,...o}],this.host._decorSel=s}this.host._decorTextDialog=null,this._recordGeometry(this.host._t(t.id?"history.decor_edit":"history.decor_add"),s),this._saveConfig(),this.host.requestUpdate()}_decorSaveShape(){const t=this.host._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),s={color:t.color,opacity:Ye(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Ye(t.fillOpacity,.25)},i=this.host._curSpaceCfg,o="furniture"===t.kind||"image"===t.kind?qt(t.sizeWField,this.host._imperial,Ut*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?qt(t.sizeHField,this.host._imperial,Ut*this.host._cellCm):null;("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a)&&(i.decor=this.host._decorList.map(e=>{if(e.id!==t.id)return e;const i="rect"===t.kind||"ellipse"===t.kind,n=Ve(s,i);if("line"===e.kind){const s=(e.x1+e.x2)/2*Wh,i=(e.y1+e.y2)/2*this.host._decorH,o=Bt(Math.max(.1,Number(t.lengthCm)||.1),this.host._cellCm,this.host._gridPitch),a=Ue(t.angle)*Math.PI/180,r=Math.cos(a)*o/2,l=Math.sin(a)*o/2,h=this._snap([s-r,i-l]),c=this._snap([s+r,i+l]),{width:d,line_style:p,...u}=e;return{...u,...n,x1:G(h[0]/Wh),y1:G(h[1]/this.host._decorH),x2:G(c[0]/Wh),y2:G(c[1]/this.host._decorH),..."dashed"===t.lineStyle?{line_style:"dashed"}:{}}}if("furniture"===e.kind){const s=e.w*Wh,i=e.h*this.host._decorH,r=Bt(Math.abs(o),this.host._cellCm,this.host._gridPitch),l=Bt(Math.abs(a),this.host._cellCm,this.host._gridPitch),h=e.x*Wh+s/2,c=e.y*this.host._decorH+i/2,d=Ue(t.angle),{width:p,angle:u,flip_h:_,flip_v:m,...g}=e;return{...g,...n,x:G((h-r/2)/Wh),y:G((c-l/2)/this.host._decorH),w:r/Wh,h:l/this.host._decorH,...t.symbol?{symbol:t.symbol}:{},...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...d?{angle:d}:{}}}if("image"===e.kind){const s=e.w*Wh,i=e.h*this.host._decorH,n=Bt(Math.abs(o),this.host._cellCm,this.host._gridPitch),r=Bt(Math.abs(a),this.host._cellCm,this.host._gridPitch),l=e.x*Wh+s/2,h=e.y*this.host._decorH+i/2,c=Ue(t.angle);return{id:e.id,kind:"image",asset_id:t.assetId||e.asset_id,x:G((l-n/2)/Wh),y:G((h-r/2)/this.host._decorH),w:n/Wh,h:r/this.host._decorH,opacity:Ye(t.opacity,1),...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...c?{angle:c}:{}}}if("rect"===e.kind||"ellipse"===e.kind){const s=e.w*Wh,i=e.h*this.host._decorH,o=Math.max(this.host._gridPitch,ue(Bt(Number(t.sizeWCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),a=Math.max(this.host._gridPitch,ue(Bt(Number(t.sizeHCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),r=e.x*Wh+s/2,l=e.y*this.host._decorH+i/2,h=Ue(t.angle),c=Xe({x:r-o/2,y:l-a/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...n,x:G(c[0]/Wh),y:G(c[1]/this.host._decorH),w:o/Wh,h:a/this.host._decorH,...h?{angle:h}:{}}}return e}),"image"!==t.kind&&this._updateDecorStyle({...s,fill:"rect"===t.kind||"ellipse"===t.kind?s.fill:this.host._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?s.fillColor:this.host._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?s.fillOpacity:this.host._decorStyle.fillOpacity}),this.host._decorShapeDialog=null,this._recordGeometry(this.host._t("history.decor_edit"),e),this._saveConfig(),this.host.requestUpdate())}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*Wh,(t.y1+t.y2)/2*this.host._decorH]:"furniture"===t.kind||"image"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*Wh,(t.y+t.h/2)*this.host._decorH]:[t.x*Wh,t.y*this.host._decorH]}_dtApply(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const i={...s};"text"===s.kind&&void 0!==e.textSizeCm&&(delete i.size,delete i.scale);const o={...i};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(Lh,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this.host._cfgEpoch++,this.host.requestUpdate())}_dtStart(t,e,s,i){const o=this.host._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[a,n]=this._dtPivot(o),r=this._svgPoint(t),l=this.host._decorBoxOf(o);this.host._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:a,ay:n,r0:Math.hypot(r[0]-a,r[1]-n),a0:180*Math.atan2(r[1]-n,r[0]-a)/Math.PI,textSizeCm0:"text"===o.kind?this.host._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:s?.[0],sgy:s?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:i,moved:!1},Gh(t)}_dtMove(t){const e=this.host._dtDrag;if(!e)return;const s=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const i=this._decorSnap(s,t.pointerType,e.id),o=G(i[0]/Wh),a=G(i[1]/this.host._decorH),n=e.origShape,r=0===e.lineEnd?n.x1:n.x2,l=0===e.lineEnd?n.y1:n.y2;return(Math.abs(o-r)>1e-9||Math.abs(a-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:a}:{x2:o,y2:a})}if("scale"===e.kind&&e.orig){if("furniture"===e.origShape.kind||"image"===e.origShape.kind){const i=Ze({...e.orig,flip_h:e.origShape.flip_h,flip_v:e.origShape.flip_v},e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,Bt(.1,this.host._cellCm,this.host._gridPitch)),o=Math.abs(i.x-e.orig.x)>1e-9||Math.abs(i.y-e.orig.y)>1e-9||Math.abs(i.w-e.orig.w)>1e-9||Math.abs(i.h-e.orig.h)>1e-9||!!i.flip_h!=!!e.origShape.flip_h||!!i.flip_v!=!!e.origShape.flip_v;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyFurnitureBox(e.id,i)}const i=Qe(e.orig,e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,this.host._gridPitch,this.host._gridPitch),o=Math.abs(i.x-e.orig.x)>1e-6||Math.abs(i.y-e.orig.y)>1e-6||Math.abs(i.w-e.orig.w)>1e-6||Math.abs(i.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,i)}if("scale"===e.kind){const t=Math.hypot(s[0]-e.ax,s[1]-e.ay);if(e.r0<1e-6)return;const i=Math.max(.1,Math.min(Lh,e.textSizeCm0*(t/e.r0))),o=Math.abs(i-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:i})}const i=180*Math.atan2(s[1]-e.ay,s[0]-e.ax)/Math.PI;let o;"furniture"===e.origShape.kind||"image"===e.origShape.kind?o=ts(e.angle0,e.a0,i,t.shiftKey):(o=e.angle0+(i-e.a0),t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360));const a=Math.abs(o-e.angle0)>1e-6;(a||e.moved)&&(e.moved||=a,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this.host._dtDrag;this.host._dtDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.decor_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_dtMeasure(){!function(t){const e=t,s=e._dtSel;if(!s)return void(e._dtBox&&(e._dtBox=null,e.requestUpdate()));if("text"!==s.kind)return;const i=e.renderRoot.querySelector(`text.dtext[data-id="${s.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let o;try{o=i.getBBox()}catch{return}if(!o||!o.width&&!o.height)return;const a={id:s.id,x:o.x,y:o.y,w:o.width,h:o.height},n=e._dtBox;n?.id===a.id&&Math.abs(n.x-a.x)<.01&&Math.abs(n.y-a.y)<.01&&Math.abs(n.w-a.w)<.01&&Math.abs(n.h-a.h)<.01||(e._dtBox=a,e.requestUpdate())}(this.host)}_deleteDecor(t){if(!this.host._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this.host._curSpaceCfg.decor=this.host._decorList.filter(e=>e.id!==t),this.host._decorSel===t&&(this.host._decorSel=null),this._recordGeometry(this.host._t("history.decor_delete"),e),this._saveConfig(),this.host.requestUpdate()}_decorDeleteSel(){this.host._decorSel&&this._deleteDecor(this.host._decorSel)}_confirmDecorErase(){const t=this.host._decorEraseConfirm;this.host._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}_furnFieldValue(t){return Math.round(100*(this.host._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return es(this.host._imperial?30.48*t:100*t)}_furnShiftAttach(){this._furnShiftAttached||(this._furnShiftAttached=!0,window.addEventListener("keydown",this._furnShiftListener),window.addEventListener("keyup",this._furnShiftListener))}_furnShiftDetach(){this._furnShiftAttached&&(this._furnShiftAttached=!1,window.removeEventListener("keydown",this._furnShiftListener),window.removeEventListener("keyup",this._furnShiftListener))}_furnPick(t){const e=ss(t);this.host._furnPalette={symbol:t,w:e.w,h:e.h},this._furnShiftAttach()}get _furnWalls(){return function(t){const e=t._spaceModel();if(!e)return[];const s=[e.id,t._cfgEpoch,t._cellCm,t._gridPitch,t._wallKeyPitch].join("|"),i=_n.get(t);if(i?.key===s)return i.value;const o=[...pn(e.rooms,t._spaceWalls,t._openCuts(),t._wallKeyPitch,t._cellCm,t._gridPitch,b),...un(t._rawPhysicalBodiesR())];return _n.set(t,{key:s,value:o}),o}(this.host)}_resolveFurniturePlacement(t,e=!1,s="mouse"){const i=this.host._furnPalette,o=this.host._curSpaceCfg;if(!i||!o)return null;const a=this.host._decorH,n=this._decorSnap(t,s);return function(t){const{symbol:e,widthCm:s,depthCm:i,point:o,canvasW:a,canvasH:n,cellCm:r,gridPitch:l,walls:h,wallReach:c,free:d=!1,intentPoint:p=o,preferredNormal:u}=t;if(!(j(e)&&n>0&&Number.isFinite(o[0])&&Number.isFinite(o[1])))return null;const _=Number.isFinite(p[0])&&Number.isFinite(p[1])?p:o,m=q(U(s,r,l,a)),g=q(U(i,r,l,a));let f=o[0],b=o[1],v=0;const y=d?null:hn(f,b,g*n,h,c,l,_,u);return y&&(f=y.cx,b=y.cy,v=y.angle),{symbol:e,x:G(f/a-m/2),y:G(b/n-g/2),w:m,h:g,angle:Number(v.toFixed(2))}}({symbol:i.symbol,widthCm:i.w,depthCm:i.h,point:[n[0],n[1]],canvasW:1e3,canvasH:a,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,walls:this._furnWalls,wallReach:6*this.host._gridPitch,intentPoint:[t[0],t[1]],free:e})}_furniturePreviewPlacement(){const t=this.host._furnPreviewInput;return t&&"decor"===this.host._mode&&"furniture"===this.host._decorTool&&this.host._furnPalette&&this.host._pointerModality.hoverEnabled?this._resolveFurniturePlacement(t.raw,t.free,"mouse"):null}_renderFurniturePlacementPreview(t){const e=this._furniturePreviewPlacement();if(!e)return d;const s=j(e.symbol);if(!s)return d;const i=is(e,Wh,this.host._decorH,s.viewW,s.viewH),o=this.host._decorStyle,a=os(Bt(o.widthCm,this.host._cellCm,this.host._gridPitch),t);return f``}_furnLive(){const t=this.host._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this.host._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const s=e.w*Wh,i=e.h*this.host._decorH,o=as(e.x*Wh,e.y*this.host._decorH,s,i,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],n=a(o[0],o[1]),r=a(o[0],o[3]);return[{x:n[0],y:n[1],text:this.host._fmtLen([0,0],[s,0])},{x:r[0],y:r[1],text:this.host._fmtLen([0,0],[0,i])}]}_furnPlace(t,e=!1,s="mouse"){const i=this._resolveFurniturePlacement(t,e,s);if(!i)return;const o=this.host._curSpaceCfg;if(!o)return;const a=this._geometrySnapshot(),n="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r={id:n,kind:"furniture",symbol:i.symbol,x:i.x,y:i.y,w:i.w,h:i.h,...Ve(this.host._decorStyle,!1)};i.angle&&(r.angle=i.angle),o.decor=[...this.host._decorList,r],this.host._decorSel=n,this.host._decorTool="select",this.host._furnPalette=null,this.host._furnCategory=null,this._furnShiftDetach(),this._clearFurniturePreview(),this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_clearFurniturePreview(){this.host._furnPreviewInput=null,this.host._furnTouchPending=null}_furnPointerMove(t,e){const s=this.host._furnTouchPending;if(s?.pid===t.pointerId)return Math.abs(t.clientX-s.sx)+Math.abs(t.clientY-s.sy)>8&&(s.cancelled=!0),this.host._furnPreviewInput=null,!0;const i="furniture"===this.host._decorTool?!!this.host._furnPalette:"image"===this.host._decorTool&&!!this.host._decorImagePalette;if("decor"!==this.host._mode||!i)return this._clearFurniturePreview(),!1;if("mouse"!==t.pointerType||!e)return this.host._furnPreviewInput=null,!1;const o=this._svgPoint(t);return this.host._furnPreviewInput={raw:[o[0],o[1]],free:t.shiftKey},!0}_furnPointerLeave(t){this.host._furnTouchPending?.pid===t.pointerId&&(this.host._furnTouchPending.cancelled=!0),this.host._furnPreviewInput=null}_furnPointerUp(t){const e=this.host._furnTouchPending;return!(!e||e.pid!==t.pointerId)&&(this.host._furnTouchPending=null,!e.cancelled&&"decor"===this.host._mode&&("furniture"===this.host._decorTool&&this.host._furnPalette||"image"===this.host._decorTool&&this.host._decorImagePalette)&&("furniture"===this.host._decorTool?this._furnPlace(this._svgPoint(t),t.shiftKey,e.pointerType):this._decorImagePlace(this._svgPoint(t),e.pointerType)),!0)}_furnMoveUpdate(t){const e=this.host._decorMove;if(!e)return;if("furniture"!==e.orig.kind)return;const s=e.orig,i=this.host._curSpaceCfg;if(!i)return;const o=Wh,a=this.host._decorH,n=this._svgPoint(t),r=(s.x+s.w/2)*o+(n[0]-e.start[0]),l=(s.y+s.h/2)*a+(n[1]-e.start[1]);let h,c,d=Number(s.angle)||0;const p=d*Math.PI/180,u=[-Math.sin(p),Math.cos(p)],_=t.shiftKey?null:hn(r,l,s.h*a,this._furnWalls,6*this.host._gridPitch,this.host._gridPitch,[r,l],u);if(_)h=_.cx/o-s.w/2,c=_.cy/a-s.h/2,d=_.angle;else{const i=this._decorSnap([r-s.w/2*o,l-s.h/2*a],t.pointerType,e.id);h=i[0]/o,c=i[1]/a}h=G(h),c=G(c),(Math.abs(h-s.x)>1e-9||Math.abs(c-s.y)>1e-9||Math.abs(d-(Number(s.angle)||0))>1e-9)&&(e.moved=!0),i.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s={...t,x:h,y:c};return d?s.angle=Number(d.toFixed(2)):delete s.angle,s}),this.host.requestUpdate()}_decorApplyBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=Wh,o=this.host._decorH;s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const a=Xe(e,s.angle,t=>this._snap(t));return{...s,x:G(a[0]/i),y:G(a[1]/o),w:Math.max(this.host._gridPitch/i,Math.min(2*Ut,e.w/i)),h:Math.max(this.host._gridPitch/o,Math.min(2*Ut,e.h/o))}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorApplyFurnitureBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=Wh,o=this.host._decorH,a=Bt(.1,this.host._cellCm,this.host._gridPitch);s.decor=this.host._decorList.map(s=>{if(s.id!==t||"furniture"!==s.kind&&"image"!==s.kind)return s;const{flip_h:n,flip_v:r,...l}=s;return{...l,x:G(e.x/i),y:G(e.y/o),w:Math.max(a/i,Math.min(2*Ut,e.w/i)),h:Math.max(a/o,Math.min(2*Ut,e.h/o)),...e.flip_h?{flip_h:!0}:{},...e.flip_v?{flip_v:!0}:{}}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorImagePlace(t,e="mouse"){this._decorImages.place(t,e)}_renderDecorImagePlacementPreview(){return this._decorImages.renderPlacementPreview()}_renderMissingDecorImage(t,e,s,i,o,a,n,r,l){return this._decorImages.renderMissing(t,e,s,i,o,a,n,r,l)}async _decorImageUpload(t,e=!1){await this._decorImages.uploadFromInput(t,e)}async _uploadDecorImage(t,e,s){await this._decorImages.upload(t,e,s)}async _decorImageDelete(t){await this._decorImages.delete(t)}async _decorImageCatalogLoad(){await this._decorImages.loadCatalog()}_renderDecorImagePalette(){return this._decorImages.renderImagePalette()}_renderFurnPalette(){return this._decorImages.renderFurniturePalette()}_openBackdropDialog(t){if(!this.host._bdMovable||!this.host._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this.host._bdDrag=null;const e=this.host._bdRect;this.host._backdropDialog={widthCm:qe(e.w,this.host._cellCm,this.host._gridPitch),heightCm:qe(e.h,this.host._cellCm,this.host._gridPitch),angle:this.host._angleField(e.angle)}}_saveBackdropDialog(){const t=this.host._backdropDialog,e=this.host._bdBase,s=this.host._bdRect;if(!t||!e||!s)return;const i=Ue(t.angle),o=this._geometrySnapshot(),a=Math.min(e.w*At,Math.max(e.w*Rt,ue(Bt(t.widthCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),n=Math.min(e.h*At,Math.max(e.h*Rt,ue(Bt(t.heightCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),r=s.x+s.w/2,l=s.y+s.h/2,h=Xe({x:r-a/2,y:l-n/2},i,t=>this._snap(t));this._bdApply((h[0]-e.x)/Wh,(h[1]-e.y)/Wh,a/e.w,n/e.h,i),this.host._backdropDialog=null,this._recordGeometry(this.host._t("history.backdrop_transform"),o),this._saveConfig()}_bdApply(t,e,s,i,o){const a=this.host._curSpaceCfg;if(!a)return;a.plan_x=Number(G(t).toFixed(6)),a.plan_y=Number(G(e).toFixed(6)),delete a.plan_scale,a.plan_scale_x=Number(Math.min(At,Math.max(Rt,s)).toFixed(6)),a.plan_scale_y=Number(Math.min(At,Math.max(Rt,i)).toFixed(6));const n=Ue(o);n?a.plan_angle=Number(n.toFixed(2)):delete a.plan_angle,this.host._cfgEpoch++,this.host.requestUpdate()}_bdStart(t,e,s=!1){const i=this.host._bdBase,o=this.host._bdRect;if(!i||!o)return!1;const a=this._svgPoint(t),n=e?e[0]:0,r=e?e[1]:0,l=n>0?o.x:o.x+o.w,h=r>0?o.y:o.y+o.h;return this.host._bdDrag={kind:s?"rotate":e?"scale":"move",pid:t.pointerId,sx:a[0],sy:a[1],base:i,p0:this.host._bdParams,fx:l,fy:h,sgx:n,sgy:r,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},Gh(t),!0}_bdMove(t){zo(this.host,"backdrop",()=>this._bdMoveNow(t))}_bdMoveNow(t){const e=this.host._bdDrag;if(!e)return;const s=this._svgPoint(t),i=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,a=this._snap([t+(s[0]-e.sx),o+(s[1]-e.sy)]),n=Math.abs(a[0]-t)>1e-9||Math.abs(a[1]-o)>1e-9;if(!n&&!e.moved)return;return e.moved||=n,void this._bdApply((a[0]-i.x)/Wh,(a[1]-i.y)/Wh,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const i=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,a=180*Math.atan2(e.sy-o,e.sx-i)/Math.PI;let n=e.p0.angle+(180*Math.atan2(s[1]-o,s[0]-i)/Math.PI-a);t.shiftKey||(n=5*Math.round(n/5)),n=Ue(n);const r=Math.abs(n-e.p0.angle)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,n)}const o=Qe(e.rect0,e.sgx,e.sgy,s[0],s[1],!t.shiftKey,this.host._gridPitch,Math.min(i.w,i.h)*Rt),a=o.w/Math.max(1e-9,i.w),n=o.h/Math.max(1e-9,i.h),r=Math.abs(a-e.p0.sx)>1e-9||Math.abs(n-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!r&&!e.moved)return;e.moved||=r;const l=Xe(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-i.x)/Wh,(l[1]-i.y)/Wh,a,n,e.p0.angle)}_bdReset(){const t=this.host._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this.host._bdDrag=null,this._recordGeometry(this.host._t("history.backdrop_transform"),e),this._saveConfig(),this.host._showToast(this.host._t("decor.backdrop_reset_done")),this.host.requestUpdate()}_bdUp(){Ro(this.host,"backdrop");const t=this.host._bdDrag;this.host._bdDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.backdrop_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_renderBackdropFrame(t){const e=this.host._bdRect;if(!this.host._bdActive||!e)return d;const s=.02*Math.max(t.w,t.h),i=s/4,o=e.x+e.w/2,a=e.y+e.h/2,n=Ue(e.angle),r=2.2*s;return f` + + + {t.stopPropagation(),t.preventDefault(),this._bdStart(t,void 0,!0)}}> + + ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,o,a])=>{const n=t<0?e.x:e.x+e.w,r=o<0?e.y:e.y+e.h;return f`{e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} + `}_renderEditorGroupLauncher(t){return this.host._editorSecondary.renderGroupLauncher(t,this.host._editorToolbarGroups,this.host._editorSecondaryCopy)}_runEditorContext(t,e){return this.host._editorSecondary.runContext(t,this.host._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this.host._editorSecondary.renderGroupModel(t,this.host._editorSecondaryContextId,this.host._editorSecondaryCopy)}_renderDrawWallControl(){const t="column"===this.host._tool?1:0;return p``}_renderPlanSecondary(){const t=this.host._editorSecondaryContextId,e=this.host._physicalSel;if(e){if(!("partition"===e.kind?!!this.host._curSpaceCfg.partitions?.some(t=>t.id===e.id):"column"===e.kind?!!this.host._curSpaceCfg.wall_columns?.some(t=>t.id===e.id):!!this.host._curSpaceCfg.room_drafts?.some(t=>t.id===e.id)))return null;const s="partition"===e.kind?this.host._t("markup.partition"):"column"===e.kind?this.host._t("markup.column"):this.host._t("markup.add");return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` + + `}}const s="draw"===this.host._tool||"column"===this.host._tool,i="column"===this.host._tool?"markup.hint_column":"resize"===this.host._tool?"markup.hint_resize":"wallthick"===this.host._tool?"markup.hint_wallthick":null,o="draw"===this.host._tool?this.host._t(this.host._path.length?"markup.hint_points":"markup.hint_start",this.host._path.length?{n:this.host._path.length}:void 0):"";if(!s&&!i&&!o)return null;const a="draw"===this.host._tool&&this.host._path.length>0;return{contextId:t,kind:a?"operation":"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("draw"===this.host._tool?"markup.add":"column"===this.host._tool?"markup.column":"resize"===this.host._tool?"markup.resize":"markup.wallthick")}),content:p` + ${s?this._renderDrawWallControl():d} + ${o?p`${o}`:d} + ${i?p`${this.host._t(i)}`:d} + ${"draw"===this.host._tool&&this.host._path.length?p``:d}`}}_renderDecorSecondary(){const t=this.host._editorSecondaryContextId;if("furniture"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.furniture")}),launcherId:"furniture",dismissPolicy:this.host._furnPalette?"stay-open-on-canvas":"outside",dismiss:()=>{"furniture"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._furnPalette=null,this._furnShiftDetach(),this.host._furnCategory=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderFurnPalette()};if("image"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.image")}),launcherId:"image",dismissPolicy:this.host._decorImagePalette?"stay-open-on-canvas":"outside",dismiss:()=>{"image"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._decorImagePalette=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderDecorImagePalette()};const e=this.host._dtSel;if(e){const s=this.host._t(`decor.${e.kind}`);return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` + + `}}const s="line"===this.host._decorTool||"rect"===this.host._decorTool||"ellipse"===this.host._decorTool,i="rect"===this.host._decorTool||"ellipse"===this.host._decorTool,o="backdrop"===this.host._decorTool&&!!this.host._bdRect;return s||o?{contextId:t,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t(s?`decor.${this.host._decorTool}`:"decor.backdrop")}),content:p` + ${s?p` + this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> + + ${i?p` + this._updateDecorStyle({...this.host._decorStyle,fillColor:t.detail.color,fillOpacity:t.detail.opacity})}>`:d} + `:d} + ${o?p`${this.host._t("decor.backdrop_hint")}`:d}`}:null}_withBackdropReset(t){if(!this.host._bdMoved||this.host._bdDrag||"palette"===t?.kind)return t;const e=this.host._editorSecondaryContextId,s=p``;return t?{...t,kind:"selection"===t.kind?"mixed":t.kind,content:p`${t.content}${s}`}:{contextId:e,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("decor.backdrop")}),content:s}}_renderEditorSecondary(){if(!this.host._editing)return d;const t=this.host._editorSecondary.activeGroup(this.host._editorToolbarGroups),e=t?this._renderEditorGroupModel(t):"plan"===this.host._mode?this._renderPlanSecondary():"decor"===this.host._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this.host._editorSecondary.render(e,this.host._editorSecondaryDialogBlocked)}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this.host._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],...this.host._haDecorAssetsApi===ns?[["image","mdi:image-plus-outline","decor.image"]]:[],["erase","mdi:eraser","decor.erase"]],e=this.host._geometryHistory.undoName,s=this.host._geometryHistory.redoName;return p`
    +
    + ${t.map(([t,e,s])=>p``)} + ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} + this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> + + + +
    +
    + +
    +
    `}_renderDecorEraseConfirm(){const t=this.host._decorEraseConfirm,e=this.host._t(`decor.${t.kind}`);return p`this.host._decorEraseConfirm=null}> +

    ${this.host._t("confirm.erase_decor",{kind:e})}

    +
    + + + +
    +
    `}_renderDecorTextDialog(){const t=this.host._decorTextDialog,e=(t.pickerEntity||"").trim(),s=e?this.host.hass?.states?.[e]:null;return p`this.host._decorTextDialog=null}> +
    + + ${""} + + this.host._decorTextDialog={...t,...e.detail}}> + +
    this.host._decorTextDialog={...t,sizeCm:this.host._decorTextCm(Number(e.target.value))}} /> + ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    + + this.host._decorTextDialog={...t,angle:e.target.value}} /> + + + this.host._decorTextDialog={...t,pickerEntity:e.target.value}} /> + + ${Object.keys(this.host.hass?.states||{}).map(t=>p``)} + + ${e?p` + + + `:d} +
    +
    + + + +
    +
    `}_renderDecorShapeDialog(){return this._decorImages.renderShapeDialog()}_renderBackdropDialog(){const t=this.host._backdropDialog,e=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`this.host._backdropDialog=null}> +
    + +
    this.host._backdropDialog={...t,widthCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ×this.host._backdropDialog={...t,heightCm:this.host._decorLargeCm(Number(e.target.value))}} /> + ${e}
    + + this.host._backdropDialog={...t,angle:e.target.value}} /> +
    +
    + + +
    +
    `}_cssPxToRender(t){const e=this.host._stageEl,s=this.host._viewOr(this.host._baseVb());return e?.clientWidth&&e.clientHeight?Math.max(s.w/e.clientWidth,s.h/e.clientHeight)*t:this.host._gridPitch/8*t}_deleteRoomClick(t){const e=this.host._spaceModel();if(!e)return;const s=[...e.rooms].reverse().find(e=>this._pointInRoom(t,e));s?s.id&&(this.host._roomDeleteDialog={roomId:s.id,name:s.name}):this.host._showToast(this.host._t("toast.delete_room_pick"))}_wallThickHit(t){const e=this.host._spaceModel();if(!e)return null;const s=6*this.host._gridPitch,i=this.host._openCuts();let o=null;const a=(t,e,i)=>{e>s||(!o||e100)return void this._showPhysicalRange(100);if("room"!==e.source.kind){const t=this._geometrySnapshot();if("partition"===e.source.kind){const t=(s.partitions||[]).find(t=>t.id===e.source.id);if(!t)return;if(0===n&&Qt(s.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));t.cm=n}else{const t=(s.room_drafts||[]).find(t=>t.id===e.source.id),i=t?.segments?.[e.source.segment];if(!i)return;i.cm=n}this.host._wallDialog=null;return this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),t)&&this.host._showToast(this.host._t("toast.wallthick_set")),void this.host.requestUpdate()}const r=this._geometrySnapshot(),l=n>0?n:null;if(0===n){const i=.02*this.host._gridPitch,o=[e.a[0],e.a[1],e.b[0],e.b[1]],a=(s.rooms||[]).find(t=>t.id===e.roomId),n=new Set(t&&a&&Array.isArray(a.wall_ids)?a.wall_ids:(s.wall_segments||[]).filter(t=>{const e=[Number(t.a?.[0])*Wh,Number(t.a?.[1])*Wh],s=[Number(t.b?.[0])*Wh,Number(t.b?.[1])*Wh];return st(e,o)<=i&&st(s,o)<=i}).map(t=>String(t.id))),r=(s.openings||[]).some(t=>{if("partition"===t.host?.kind)return!1;if("wall"===t.host?.kind&&n.has(t.host.id))return!0;if(t.host)return!1;const e=[Number(t.x)*Wh,Number(t.y)*Wh];return e.every(Number.isFinite)&&st(e,o)<=i});if(r)return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"))}let h,c=this.host._openCuts();if(n>0&&c.length){const o=[];if(t&&e.roomId){const t=(s.rooms||[]).find(t=>t.id===e.roomId),a=new Set(Array.isArray(t?.wall_ids)?t.wall_ids:[]);for(const t of s.wall_segments||[])a.has(t.id)&&o.push([Number(t.a?.[0])*Wh,Number(t.a?.[1])*Wh,Number(t.b?.[0])*Wh,Number(t.b?.[1])*Wh]);if(!o.length){const t=i.rooms.find(t=>t.id===e.roomId),s=t?at(t):null;for(let t=0;s&&t!o.some(e=>st([t[0],t[1]],e)<=a&&st([t[2],t[3]],e)<=a&&st([e[0],e[1]],t)<=a&&st([e[2],e[3]],t)<=a))}h=t&&e.roomId?rs(s.walls,i.rooms,e.roomId,l,this.host._wallKeyPitch,c,Wh):pt(s.walls,e.a,e.b,l,this.host._wallKeyPitch,Wh),h=this.host._normalizeWalls(h,c);for(const t of s.wall_segments||[])t.cm=St(h,t.a,t.b,W,1);h.length?s.walls=h:delete s.walls,this.host._wallDialog=null,this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),r)&&this.host._showToast(this.host._t(0===n?"toast.wallthick_cleared":"toast.wallthick_set")),this.host.requestUpdate()}_wallHatchDefs(t){if(!this.host._spaceWalls.length&&!this.host._physicalBodiesR().length&&!this.host._markup)return f``;const e=w(this.host._cellCm);return f` + + + + `}_renderWallThickUi(){const t=this.host._wallThickHover;return t&&t.d?f``:f``}_renderWallThickDialog(){const t=this.host._wallDialog;return t?p`
    t.stopPropagation()}> +
    + + {this.host._wallDialog={...t,value:e.target.value}}} + @keydown=${t=>{"Enter"===t.key&&(t.preventDefault(),this._wallThickApply(!1))}} /> + ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")} +
    +
    + ${"room"===t.source.kind?p``:d} + + +
    +
    `:p``}_openingAt(t){if(!this.host._openingsR.length)return null;const e=this.host._openingsR.flatMap(e=>{const s=e.angle*Math.PI/180,i=t[0]-e.rx,o=t[1]-e.ry,a=i*Math.cos(s)+o*Math.sin(s);return Math.abs(a)>e.rlen/2+k(12,this.host._cellCm)?[]:[{o:e,localY:-i*Math.sin(s)+o*Math.cos(s)}]});if(!e.length)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i).value;return e.find(({o:t,localY:e})=>{const s="gate"===t.type?!t.flip_v:t.flip_v,i=t.partitionHost||this.host._spaceWalls.length||"gate"===t.type?this.host._openingFace(t,o,!!s):{ox:0,oy:0,cm:0,side:-1},a=ls({type:t.type,length:t.rlen,angle:t.angle,amount:this.host._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:i});return Math.abs(e)<=a.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this.host._openingPreset;if("opening"!==this.host._tool||!e)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i),a=`${o.key}|partitions:${this.host._cfgEpoch}`;this.host._openingPlacementIntervalsCache&&this.host._openingPlacementIntervalsCache.key===a||(this.host._openingPlacementIntervalsCache={key:a,value:[...lt(s.rooms,this.host._spaceWalls,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh),...hs(s.partitions,this.host._cellCm,this.host._gridPitch)]}),this.host._openingDimensionContextCache&&this.host._openingDimensionContextCache.key===a||(this.host._openingDimensionContextCache={key:a,value:Dh({rooms:s.rooms,walls:this.host._spaceWalls,openCuts:i,partitions:s.partitions,roomOpenings:this.host._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(s),pitch:this.host._wallKeyPitch,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,coordScale:Wh,epsilon:2e-4*this.host._gridPitch})});const n=cs({pointer:[t[0],t[1]],preset:e,geometryRevision:this.host._cfgEpoch,renderedLength:this.host._cmToUnits(e.lengthCm),intervals:this.host._openingPlacementIntervalsCache.value,baseTolerance:1.5*this.host._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this.host._pointerModality.modality||"pen"===this.host._pointerModality.modality?10:6),gridStep:this.host._gridPitch});this.host._openingJambBlockCm=n.jambBlockedTarget?n.jambBlockedTarget.physicalHalfWidth/this.host._gridPitch*this.host._cellCm:null;const r=n.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let h;if(r.host){const t=nt({id:"preview",type:r.type,x:r.x/Wh,y:r.y/Wh,angle:r.angle,length:r.renderedLength/Wh,host:r.host},s.partitions,Wh,this.host._cellCm,this.host._gridPitch).resolved;h=t?ds(t,l):{ox:0,oy:0,cm:0,side:-1}}else h=ps(o.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const c="mi"===this.host.hass?.config?.unit_system?.length,d=Ah(r,this.host._openingDimensionContextCache.value).map(t=>({x:t.label[0],y:t.label[1],text:Te(t.distance/this.host._gridPitch*this.host._cellCm,c),dimension:t}));return{...r,face:h,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(t){this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingPreset=us(t,++this.host._openingPresetRevision),this.host._openingHoverCandidate=null,this.host._cursorPt=null)}_clearOpeningPlacement(t){this.host._openingHoverCandidate=null,this.host._openingJambBlockCm=null,t&&(this.host._openingPreset=null,this.host._openingRebindId=null)}_openingClick(t){const e=this.host._spaceModel();if(!e)return;const s=this.host._openingRebindId?null:this._openingAt(t);if(s)return void this._editOpening(s);const i=this.host._openingPreset;if(!i)return;const o=this.host._openingHoverCandidate,a=o&&_s(o,[t[0],t[1]],i.revision,this.host._cfgEpoch)?o:this._resolveOpeningPlacement(t);if(!a){if(null!=this.host._openingJambBlockCm)return void this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Te(this.host._openingJambBlockCm,this.host._imperial)}));const s=1.5*this.host._gridPitch,i=it(t,e.rooms,s);return i&&ms(i.x,i.y,i.angle,this.host._openCuts(),s)?void this.host._showToast(this.host._t("toast.opening_on_zero_wall")):void this.host._showToast(this.host._t("toast.opening_no_wall"))}const n=this.host._openingRebindId?this.host._curSpaceCfg?.openings?.find(t=>t.id===this.host._openingRebindId):null;this.host._openingDialog={...n?{id:n.id,type:n.type,lengthCm:Math.round(n.length*Wh/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:n.contact||"",lock:n.lock||"",invert:!!n.invert,flipH:!!n.flip_h,flipV:!!n.flip_v}:{type:a.type,lengthCm:a.lengthCm,contact:"",lock:"",invert:!1,flipH:a.flipH,flipV:a.flipV},...a.host?{host:a.host}:{},x:a.x,y:a.y,angle:a.angle},this.host._openingRebindId=null,this.host._openingHoverCandidate=null,this.host._cursorPt=null}_editOpening(t){this.host._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,..."partition"===t.host?.kind?{host:{...t.host}}:{},x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this.host._mode&&this.host._spaceModel()&&"resize"!==this.host._tool){t.preventDefault(),t.stopPropagation();try{Gh(t)}catch{}this.host._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){zo(this.host,"opening",()=>this._opPointerMoveNow(t,e))}_opPointerMoveNow(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this.host._opDrag.sx)+Math.abs(t.clientY-this.host._opDrag.sy)<=3)return;const s=this.host._spaceModel();if(!s)return;const i=this._svgPoint(t),o=this.host._curSpaceCfg,a=o?.openings?.find(t=>t.id===e.id);if(!a)return;if("partition"===a.host?.kind){const t=s.partitions.find(t=>t.id===a.host.id);if(!t)return;const e=t.b[0]-t.a[0],o=t.b[1]-t.a[1],n=Math.hypot(e,o);if(!(n>1e-9))return;const r=e/n,l=o/n;if(Math.abs((i[0]-t.a[0])*l-(i[1]-t.a[1])*r)>4*this.host._gridPitch)return;const h=a.length*Wh/2+dt(t,this.host._cellCm,this.host._gridPitch);let c=(i[0]-t.a[0])*r+(i[1]-t.a[1])*l;if(c=Math.round(c/this.host._gridPitch)*this.host._gridPitch,c=Math.max(h,Math.min(n-h,c)),n<2*h-1e-9)return;const d=c/n,p=(t.a[0]+r*c)/Wh,u=(t.a[1]+l*c)/this.host._spaceH;let _=180*Math.atan2(o,e)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=a.host.t!==d||a.x!==p||a.y!==u||a.angle!==_;return this.host._opDrag.moved=!0,m&&(this.host._opDrag.dirty=!0),a.host={...a.host,t:d},a.x=p,a.y=u,a.angle=_,this.host._opMeasure=null,m&&this.host._cfgEpoch++,void this.host.requestUpdate()}const n=it(i,s.rooms,4*this.host._gridPitch);if(!n)return;this.host._opDrag.moved=!0;const r=this._opRuler(n,a.length*Wh);this.host._opMeasure=r.measure;const l=r.x/Wh,h=r.y/this.host._spaceH,c=a.x!==l||a.y!==h||a.angle!==n.angle;if(c&&(this.host._opDrag.dirty=!0),a.x=l,a.y=h,a.angle=n.angle,Number(this.host._serverCfg?.model_version||0)>=9){const t=Vl(a,o?.wall_segments||[]);t?a.host=t:delete a.host}c&&this.host._cfgEpoch++,this.host.requestUpdate()}_opRuler(t,e){const s=this.host._spaceModel()?.rooms||[],i=this.host._gridPitch/2;let o=t.x,a=t.y,n=gs([o,a],t.angle,e,s,i);if(n&&n.centered&&(o!==n.wallCenter[0]||a!==n.wallCenter[1]))[o,a]=n.wallCenter,n=gs([o,a],t.angle,e,s,i);else if(n){const[r,l]=n.wallA,[h,c]=n.wallB,d=h-r,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this.host._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-r)*d+(a-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=r+_/u*d,a=l+_/u*p,n=gs([o,a],t.angle,e,s,i)||n}}if(!n)return{x:o,y:a,angle:t.angle,measure:null};const r="mi"===this.host.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Te(t/this.host._gridPitch*this.host._cellCm,r)});return{x:o,y:a,angle:t.angle,measure:{labels:[l(n.sideA,n.midA),l(n.sideB,n.midB)],guide:n.centered?{x:n.wallCenter[0],y:n.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;Ro(this.host,"opening");const s=this.host._opDrag,i=s.moved;this.host._opMeasure=null,i&&s.dirty&&this._commitPhysicalGeometry(this.host._t("history.move_opening"),s.before);const o=()=>{this.host._opDrag=null,this.host.requestUpdate()};i?window.setTimeout(o,0):o()}_opClick(t,e){"plan"===this.host._mode&&"resize"===this.host._tool||(t.stopPropagation(),this.host._opDrag?.moved||"plan"===this.host._mode&&this._editOpening(e))}_saveOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=this._geometrySnapshot(),o=this.host._spaceH,a=(e.openings||[]).find(e=>e.id===t.id),n={...a||{},id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Wh,y:t.y/o,angle:t.angle,length:a&&!t.lengthTouched?a.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/Wh,...t.host?{host:{...t.host}}:{}};if("partition"===n.host?.kind){const t=fs(a,n)?_t(n,s.partitions,Wh,this.host._cellCm,this.host._gridPitch):nt(n,s.partitions,Wh,this.host._cellCm,this.host._gridPitch);if(!t.resolved){const e=s.partitions.find(t=>t.id===n.host.id);if("does-not-fit-jamb"===t.reason&&e){const t=dt(e,this.host._cellCm,this.host._gridPitch);this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Te(t/this.host._gridPitch*this.host._cellCm,this.host._imperial)}))}else this.host._showToast(this.host._t("opening.partition_orphan"));return}const i=(e.openings||[]).flatMap(t=>{if(!t.host||t.id===n.id)return[];const e=nt(t,s.partitions,Wh,this.host._cellCm,this.host._gridPitch).resolved;return e?[e]:[]});if(bs(t.resolved,i))return void this.host._showToast(this.host._t("toast.opening_no_wall"));Object.assign(n,rt(n,t.resolved,Wh))}else if(Number(this.host._serverCfg?.model_version||0)>=9){const t=Vl(n,e.wall_segments||[]);if(!t)return void this.host._showToast(this.host._t("toast.opening_no_wall"));n.host=t}else delete n.host;"passage"===t.type?(delete n.contact,delete n.lock,delete n.invert,delete n.flip_h,delete n.flip_v):(n.contact=t.contact||null,n.lock=("door"===t.type||"gate"===t.type)&&t.lock||null,n.invert=t.invert||void 0,n.flip_h="gate"!==t.type&&t.flipH||void 0,n.flip_v=t.flipV||void 0),e.openings=e.openings||[];const r=e.openings.findIndex(t=>t.id===n.id);r>=0?e.openings[r]=n:e.openings.push(n),this._commitPhysicalGeometry(this.host._t(t.id?"history.edit_opening":"history.add_opening"),i),this.host._openingDialog=null,this.host.requestUpdate()}_deleteOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg;if(!t?.id||!e?.openings)return;const s=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._commitPhysicalGeometry(this.host._t("history.delete_opening"),s),this.host._openingDialog=null,this.host.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.host.hass.states)){if(!this.host._openingEntityAvailable(e))continue;const s=e.split(".")[0];if("binary_sensor"!==s&&"cover"!==s)continue;const i=this.host.hass.states[e],o=["door","window","opening","garage_door","garage"].includes(i?.attributes?.device_class||"");("cover"!==s||o)&&t.push([e,i?.attributes?.friendly_name||e,o?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.host.hass.states).filter(t=>t.startsWith("lock.")&&this.host._openingEntityAvailable(t)).map(t=>({value:t,label:this.host.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_toggleOpeningEntityPicker(t){const e=this.host._openingDialog;if(!e)return;const s="contact"===t?!e.contactOpen:!e.lockOpen;this.host._openingDialog={...e,contactOpen:"contact"===t&&s,lockOpen:"lock"===t&&s}}_filterOpeningEntities(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contactFilter:e}:{...s,lockFilter:e})}_selectOpeningEntity(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contact:e,contactOpen:!1,contactFilter:""}:{...s,lock:e,lockOpen:!1,lockFilter:""})}_mergeClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms,i=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const o=i.id;if(!this.host._mergeSel||this.host._mergeSel===o)return void(this.host._mergeSel=this.host._mergeSel===o?null:o);const a=s.find(t=>t.id===this.host._mergeSel),n=a?at(a):null,r=at(i),l=n&&r?vs(n,r):null;if(!l)return this.host._showToast(this.host._t("toast.merge_not_adjacent")),void(this.host._mergeSel=null);this.host._mergeDialog={aId:this.host._mergeSel,bId:o,poly:l,pick:"a"},this.host._mergeSel=null}_commitMerge(){const t=this.host._mergeDialog,e=this.host._curSpaceCfg;if(!t||!e||!this.host._spaceModel())return;const s=this._geometrySnapshot(),i=this.host._spaceH,o="a"===t.pick?t.aId:t.bId,a="a"===t.pick?t.bId:t.aId,n=e.rooms.find(t=>t.id===o);if(!n)return void(this.host._mergeDialog=null);n.poly=t.poly.map(t=>[t[0]/Wh,t[1]/i]),delete n.x,delete n.y,delete n.w,delete n.h,e.rooms=e.rooms.filter(t=>t.id!==a);const r=this._commitPhysicalGeometry(this.host._t("history.merge_rooms"),s);this.host._mergeDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),r&&this.host._showToast(this.host._t("toast.rooms_merged",{name:n.name||""}))}_splitClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms;if(!this.host._splitSel){const e=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this.host._splitSel={roomId:e.id,pts:[]})}const i=s.find(t=>t.id===this.host._splitSel.roomId),o=i?at(i):null;if(!i||!o)return void(this.host._splitSel=null);const a=.02*this.host._gridPitch,n=6*this.host._gridPitch,r=ys(t,o),l=r&&$s(r,o,this.host._gridPitch)||r,h=r&&l&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?l:null,c=!!h&&ws(h,o,a),d=this.host._splitSel.pts;if(!d.length)return c?void(this.host._splitSel={...this.host._splitSel,pts:[h]}):void this.host._showToast(this.host._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return we(e,o,a)?void(this.host._splitSel={...this.host._splitSel,pts:[...d,e]}):void this.host._showToast(this.host._t("toast.split_pick_inside"))}const p=xe(o,[...d,h],a);if(!p)return void this.host._showToast(this.host._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=B(u)>=B(_)?u:_,g=m===u?_:u;this.host._pendingSplit={roomId:i.id,mainPoly:m,newPoly:g},this.host._cursorPt=null,this.host._nameSel="",this.host._areaSel="",this.host._roomDialog=!0}_markupMove(t){if(!this.host._markup)return;if("column"===this.host._tool)return void(this.host._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this.host._tool||"wallthick"===this.host._tool)return void(this.host._cursorPt=this._svgPoint(t));const e="draw"===this.host._tool&&!this.host._contourClosed,s="split"===this.host._tool&&!!this.host._splitSel?.pts?.length;if(!e&&!s)return;const i=this._svgPoint(t);if(e){const e=this._resolvePlanDrawPoint(i,t.shiftKey);if(this.host._planSnapHover={contextKey:e.contextKey,candidate:e.candidate,conflicts:e.conflicts},this._syncPlanSnapConflictMarkers(e.conflicts),this._syncPlanSnapActiveMarker(e.candidate),!this.host._path.length)return;return void(this.host._cursorPt=e.point)}this.host._cursorPt=this._snap(i)}_saveRoom(){(this.host._areaSel||this.host._nameSel.trim())&&(this.host._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(t){const e=this.host._wallFaceBatch;if(!e)return;const s=e.candidates[e.index];if(!s)return;if(!t&&(s.existing||s.repair)&&1===e.candidates.length)return this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate();const i=t?{candidate:s,create:!0,name:this.host._nameSel.trim(),area:this.host._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:s,create:!1},o=[...e.decisions,i],a=e.index+1;if(a"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,o=.02*this.host._gridPitch,a=this.host._spaceModel();if(!a)return ht;for(const e of this._planSnapGeometrySnapshot().value.segments){if("room"===e.sourceKind||st(t,[e.a[0],e.a[1],e.b[0],e.b[1]])>o)continue;if("partition"===e.sourceKind)return i(a.partitions.find(t=>t.id===e.sourceId)?.cm)??ht;const s=e.sourceId.lastIndexOf(":"),n=s>=0?e.sourceId.slice(0,s):e.sourceId,r=s>=0?Number(e.sourceId.slice(s+1)):-1;return i(a.room_drafts.find(t=>t.id===n)?.segments[r]?.cm)??ht}const n=or(Math.max(0,e.length-1),s,this.host._drawWallCm,ht);for(let s=0;s+1[...t]);if(!e?.sourceKey.startsWith("active:"))return s;const i=/:(\d+)$/.exec(e.sourceKey),o=i?Number(i[1])+("b"===e.endpoint?1:0):-1;return o>=0&&oe.key===t.sourceKey),a=i.find(e=>e.key===t.targetSourceKey);if(!o||!a)return!1;const n="a"===t.endpoint?o.a:o.b;return!(Math.hypot(n[0]-t.from[0],n[1]-t.from[1])>s)&&(!(st(t.to,[a.a[0],a.a[1],a.b[0],a.b[1]])>s)&&!function(t,e){if(!t.sourceKey.startsWith("static:partition|"))return!1;const s=t.sourceKey.slice(17).split("|")[0];return!!s&&e.some(t=>"partition"===t.host?.kind&&t.host.id===s)}(t,this.host._curSpaceCfg?.openings||[]))}_applyWallRepair(t,e){const s=this.host._curSpaceCfg;if(!s||!this._validateWallRepair(t,e.activePath))return!1;const i=e=>{e[0]=t.to[0]/Wh,e[1]=t.to[1]/Wh};if(t.sourceKey.startsWith("active:")){const o=/:(\d+)$/.exec(t.sourceKey),a=o?Number(o[1])+("b"===t.endpoint?1:0):-1;if(a<0||a>=e.activePath.length)return!1;e.activePath[a]=[...t.to];const n=(s.room_drafts||[]).find(t=>t.id===e.activeDraftId);return n?.points?.[a]&&i(n.points[a]),!0}if(!t.sourceKey.startsWith("static:"))return!1;const[o,a]=t.sourceKey.slice(7).split("|");if("partition"===o){const e=(s.partitions||[]).find(t=>t.id===a);if(!e)return!1;const o=[e.a[0]*Wh,e.a[1]*Wh],n=[e.b[0]*Wh,e.b[1]*Wh],r=Math.hypot(o[0]-t.from[0],o[1]-t.from[1])<=2e-4*this.host._gridPitch?e.a:Math.hypot(n[0]-t.from[0],n[1]-t.from[1])<=2e-4*this.host._gridPitch?e.b:null;return!!r&&(i(r),!0)}if("draft"===o){const e=a.lastIndexOf(":"),o=e>=0?a.slice(0,e):"",n=e>=0?Number(a.slice(e+1)):-1,r=(s.room_drafts||[]).find(t=>t.id===o);if(!r?.points?.[n]||!r?.points?.[n+1])return!1;const l=[n,n+1].find(e=>Math.hypot(r.points[e][0]*Wh-t.from[0],r.points[e][1]*Wh-t.from[1])<=2e-4*this.host._gridPitch);return null!=l&&(i(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const t=this.host._wallFaceBatch,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=(t,e)=>{this.host._showToast(this.host._t(t,e)),this._roomDialogCancel()},o=t.decisions.filter(t=>t.create),a=o.map(t=>t.candidate.ring),n=s.rooms;for(let t=0;ts.id!==e&&!!at(s)&&E(a[t],at(s)));if(s)return void i("toast.room_overlap",{name:s.name||""})}if((e.rooms||[]).length+o.length>400)return void i("toast.physical_limit");if(o.some(t=>t.candidate.split&&!e.rooms.some(e=>e.id===t.candidate.split.roomId)))return void i("toast.contour_cannot_close");const r=o.map(t=>t.candidate.repair).filter(t=>!!t);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],t.activePath))return void i("toast.wall_repair_changed");const l=this._activePathWithRepair(t.activePath,r[0]),h=2e-4*this.host._gridPitch,c=new Map;for(let e=0;et.candidate.atomKeys)),e=o.some(t=>t.candidate.consumeAllActive);for(const s of Mr(this._wallGraphSources(l),h)){const i=s.sourceKeys.find(t=>c.has(t));!i||e||t.has(s.key)||d.push({a:s.a,b:s.b,cm:c.get(i)})}}else d.push(...ar(l,or(l.length-1,t.activeCms,this.host._drawWallCm,ht)));if((e.partitions||[]).length+d.length>jh)return void i("toast.physical_limit");const p=o.map(t=>t.candidate.ring.map(()=>"")),u=t.activeDraftId?(e.room_drafts||[]).find(e=>e.id===t.activeDraftId):null,_=this._draftSegmentsForPath(t.activePath,u,t.activeCms),m=(t,e)=>{const s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`;return s<=i?`${s}|${i}`:`${i}|${s}`};for(let e=0;e<_.length;e++){const s=_[e]?.id;if(!s)continue;const i=t.activePath[e],a=t.activePath[e+1],n=[i[0],i[1],a[0],a[1]],r=[];o.forEach((t,e)=>t.candidate.ring.forEach((s,i)=>{const o=t.candidate.ring[(i+1)%t.candidate.ring.length];st(s,n)<=h&&st(o,n)<=h&&r.push({kind:"room",owner:e,edge:i,a:s,b:o})})),d.forEach((t,e)=>{st(t.a,n)<=h&&st(t.b,n)<=h&&r.push({kind:"partition",owner:e,edge:0,a:t.a,b:t.b})});const l=[(i[0]+a[0])/2,(i[1]+a[1])/2];r.sort((t,e)=>{const s=[t.a[0],t.a[1],t.b[0],t.b[1]],o=[e.a[0],e.a[1],e.b[0],e.b[1]];return(st(l,s)<=h?0:1)-(st(l,o)<=h?0:1)||(st(i,s)<=h?0:1)-(st(i,o)<=h?0:1)||m(t.a,t.b).localeCompare(m(e.a,e.b))});const c=r[0];if(!c)continue;const u=m(c.a,c.b);for(const t of r)m(t.a,t.b)===u&&("room"===t.kind?p[t.owner][t.edge]=s:d[t.owner].id=s)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],t))return void i("toast.wall_repair_changed");const f=o.some(t=>!!t.candidate.split),b=f?ee(s.rooms,e.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh):null,v=f&&Array.isArray(e.walls)?e.walls:null;for(const t of o){const s=t.candidate.split;if(!s)continue;const o=e.rooms.find(t=>t.id===s.roomId);if(!o)return void i("toast.contour_cannot_close");o.poly=s.mainPoly.map(t=>[t[0]/Wh,t[1]/this.host._spaceH]),delete o.x,delete o.y,delete o.w,delete o.h}const y=Date.now().toString(36),$=[];if(o.forEach((t,s)=>{const i=t.area?this.host.hass.areas[t.area]?.name:"",o={id:`r${y}-${s}`,name:t.name||i||this.host._t("room.default_name"),area:t.area||null,poly:t.candidate.ring.map(t=>[t[0]/Wh,t[1]/this.host._spaceH]),...p[s].some(Boolean)?{wall_ids:p[s]}:{},...t.settings?{settings:JSON.parse(JSON.stringify(t.settings))}:{}};e.rooms.push(o),$.push({room:o,decision:t})}),t.activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==t.activeDraftId),e.room_drafts.length||delete e.room_drafts),d.length&&(e.partitions||=[],d.forEach((t,s)=>e.partitions.push({id:t.id||`partition-${y}-${s}`,a:[t.a[0]/Wh,t.a[1]/Wh],b:[t.b[0]/Wh,t.b[1]/Wh],cm:t.cm}))),f){const t=this.host._normalizeWalls(b,this.host._openCuts());t.length?e.walls=t:v?.length?e.walls=v:delete e.walls}if($.length){this.host._cfgEpoch++;const s=this.host._openCuts(),i=this.host._spaceModel();if(!i)return;let o=e.walls;for(const{room:e,decision:a}of $){const n=a.candidate.ring,r=this._wallSourceCmAt(n[0],t.activePath,t.activeCms);o=ks(o,i.rooms,e.id,r,this.host._wallKeyPitch,s,Wh);for(const a of lt(i.rooms,o,s,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh)){if(a.roomId!==e.id||"outer"!==a.kind)continue;const s=[(a.a[0]+a.b[0])/2,(a.a[1]+a.b[1])/2];o=pt(o,a.a,a.b,this._wallSourceCmAt(s,t.activePath,t.activeCms),this.host._wallKeyPitch,Wh)}}const a=this.host._normalizeWalls(o,s);a.length?e.walls=a:delete e.walls}if(!this._commitPhysicalGeometry(this.host._t("history.wall_face_batch"),g))return;delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices();const w=new Set(o.map(t=>t.area).filter(t=>!!t));if(w.size){const t={...this.host._layout};for(const e of this.host._devices){if(!w.has(e.area||"")||e.space!==this.host._space)continue;if(this.host._layout[e.id])continue;const s=this.host._defPos[e.id];s&&(t[e.id]={s:this.host._space,x:s.x/Wh,y:s.y/Wh},this.host._dirtyPos.add(e.id))}this.host._layout=t,this.host._persistLayout()}this.host._showToast(this.host._t(o.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:o.length}))}_commitRoom(){const t=this.host._curSpaceCfg,e=this.host._spaceModel();if(!t||!e)return;const s=this._geometrySnapshot(),i=this.host._spaceH,o=!!this.host._pendingSplit,a=o?ee(e.rooms,t.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh):null,n=o&&Array.isArray(t.walls)?t.walls:null;let r;if(this.host._pendingSplit){const e=t.rooms.find(t=>t.id===this.host._pendingSplit.roomId);if(!e)return this.host._pendingSplit=null,this.host._splitSel=null,void(this.host._roomDialog=!1);e.poly=this.host._pendingSplit.mainPoly.map(t=>[t[0]/Wh,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this.host._pendingSplit.newPoly}else{if(!this.host._contourClosed)return;r=this.host._path.slice(0,-1)}const l=this.host._areaSel?this.host.hass.areas[this.host._areaSel]?.name:"",h={id:"r"+Date.now().toString(36),name:this.host._nameSel||l||this.host._t("room.default_name"),area:this.host._areaSel||null,poly:r.map(t=>[t[0]/Wh,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!o&&this.host._activeDraftId){const e=Array.isArray(t.room_drafts)?t.room_drafts.find(t=>t.id===this.host._activeDraftId):null,s=or(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,ht),i=this._draftSegmentsForPath(this.host._path,e,s);i.length===r.length&&i.every(t=>!!t.id)&&(h.wall_ids=i.map(t=>t.id))}if(t.rooms.push(h),!o&&this.host._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),o){const e=this.host._normalizeWalls(a,this.host._openCuts());e.length?t.walls=e:n?.length?t.walls=n:delete t.walls}if(!o){const e=or(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,ht),s=e[0];if(null!=s){this.host._cfgEpoch++;const i=this.host._openCuts(),o=this.host._spaceModel();if(!o)return;let a=ks(t.walls,o.rooms,h.id,s,this.host._wallKeyPitch,i,Wh);for(const t of lt(o.rooms,a,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh)){if(t.roomId!==h.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],i=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return st(s,[t[0],t[1],i[0],i[1]])<=.02*this.host._gridPitch});i>=0&&(a=pt(a,t.a,t.b,e[i],this.host._wallKeyPitch,Wh))}a=this.host._normalizeWalls(a,i),a.length?t.walls=a:delete t.walls}}const c=this._commitPhysicalGeometry(this.host._t(o?"history.split_room":"history.add_room"),s);this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._pendingSplit=null,this.host._splitSel=null;const d=this.host._areaSel;if(this.host._areaSel="",this.host._nameSel="",this.host._roomDialog=!1,this.host._regSignature="",this.host._maybeRebuildDevices(),!c)return;let p=0;if(d){const t=Wh,e={...this.host._layout};for(const s of this.host._devices){if(s.area!==d||s.space!==this.host._space)continue;if(p++,this.host._layout[s.id])continue;const i=this.host._defPos[s.id];i&&(e[s.id]={s:this.host._space,x:i.x/Wh,y:i.y/t},this.host._dirtyPos.add(s.id))}this.host._layout=e,this.host._persistLayout()}const u=this.host._model.find(t=>t.id===this.host._space)?.rooms.length||0;this.host._showToast(d?this.host._t("toast.room_saved",{n:u,added:p}):this.host._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this.host._wallRepairDiagnostic=null,this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),this.host._roomDialog=!1,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._splitSel=null,this.host._mergeSel=null,this.host._mergeDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this.host._resumeDraftBySpace[this.host._space];if(!t)return;const e=this.host._spaceModel()?.room_drafts.find(e=>e.id===t);e?(this.host._activeDraftId=t,this.host._path=e.points.map(t=>[...t]),this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.segments.map(t=>t.cm),e.id),this._clearPlanSnapHover()):delete this.host._resumeDraftBySpace[this.host._space]}_roomDialogCancel(){if(this.host._roomDialog=!1,this.host._roomEditId)return this.host._roomEditId=null,this.host._nameSel="",void(this.host._areaSel="");if(this.host._wallFaceBatch){const t=this.host._wallFaceBatch;return this.host._wallFaceBatch=null,this.host._path=t.activePath.map(t=>[...t]),this.host._draftSegmentCms=[...t.activeCms],this.host._activeDraftId=t.activeDraftId,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate()}if(this.host._pendingSplit)return this.host._pendingSplit=null,void(this.host._splitSel=null);this.host._undoPoint()}_openDeviceInbox(){this.host._deviceInboxReturn=null,this.host._deviceInbox=this.host._deviceInbox||{tab:"on_plan",search:"",showEntities:!1,onlyNew:!1,limit:100}}_closeMarkerDialog(){if(this.host._markerDialog=null,this.host._deviceInboxReturn){const t={...this.host._deviceInboxReturn};this.host._deviceInbox=t,this.host._deviceInboxReturn=null,t.anchor&&this.host.updateComplete.then(()=>requestAnimationFrame(()=>{const e=`.device-inbox-row[data-binding="${CSS.escape(t.anchor)}"]`;this.host.renderRoot.querySelector(e)?.scrollIntoView({block:"nearest"})}))}}_deviceInboxCandidates(t){return Nn({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:t,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}})}_deviceInboxRows(){const t=this.host._deviceInbox||this.host._deviceInboxReturn,e=!!t?.showEntities,s=[this.host._haRegistry.revision,this.host._cfgRev,this.host._cfgEpoch,this.host._regSignature,this.host._newSyncKey,e?1:0,this.host._showAll?1:0,l(this.host.hass,this.host._config?.language)].join("|");if(this.host._deviceInboxMemo?.key===s)return this.host._deviceInboxMemo.rows;const i=this._deviceInboxCandidates(e),o=new Set(i.map(t=>t.value));for(const t of this.host._markers)t.binding&&"virtual"!==t.binding&&o.add(t.binding);for(const t of this.host._devices)t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&o.add(`${t.bindingKind}:${t.bindingRef}`);const a=new Map;for(const t of o)a.set(t,this.host._bindingStatus(t));const n=this.host._areaToSpace,r={};for(const[t,e]of Object.entries(this.host.hass?.areas||{}))r[t]=n[t]?.room?.name||e?.name||t;for(const[t,e]of Object.entries(n))r[t]=e.room.name||r[t]||t;const h=Object.fromEntries(this.host._model.map(t=>[t.id,t.title])),c=Object.fromEntries(Object.entries(n).map(([t,e])=>[t,e.space])),d={},p=new Map;for(const[t,e]of Object.entries(this.host._fullRegistryHass.entities||{})){const s=String(e?.platform||"").trim();if(s&&(d[`entity:${t}`]=s),s&&e?.device_id){const t=p.get(e.device_id)||new Set;t.add(s),p.set(e.device_id,t)}}for(const[t,e]of p)d[`device:${t}`]=[...e].sort().join(", ");const u={};for(const[t,e]of Object.entries(this.host._fullRegistryHass.devices||{})){const s=`device:${t}`,i=p.get(t)||new Set,o=Array.isArray(e?.identifiers?.[0])?String(e.identifiers[0][0]||""):"";o&&!d[s]&&(d[s]=o);const a=[o,...i].some(t=>this.host._excluded.has(t));"service"===e?.entry_type?u[s]="service_entry":a?u[s]="excluded_integration":"Group"===e?.model?u[s]="grouped_light":/scene/i.test(e?.model||"")?u[s]="excluded_domain":(/bridge/i.test(`${e?.model||""}${e?.name||""}`)||"myheat"===o&&e?.via_device_id)&&(u[s]="represented_by_parent")}const _=function(t){const{devices:e,markers:s,candidates:i,statuses:o,newDeviceIds:a,showHiddenOnPlan:n,areaNames:r={},spaceNames:l={},spaceByArea:h={},integrationByBinding:c={},reasonByBinding:d={}}=t,p=new Map;for(const t of e)!t.virtual&&t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&p.set(`${t.bindingKind}:${t.bindingRef}`,t);const u=new Map,_=new Map;for(const t of s)t?.binding&&"virtual"!==t.binding&&(t.removed?_.set(t.binding,t):u.set(t.binding,t));const m=new Map(i.map(t=>[t.value,t])),g=new Set([...p.keys(),...u.keys(),...m.keys(),...[..._.keys()].filter(t=>m.has(t))]),f=[];for(const t of g){const e=p.get(t),s=u.get(t),i=s?void 0:_.get(t),g=m.get(t);let b;if(i&&g)b="readd";else if(!0===s?.hidden)b="hidden";else if(e||s)b="on_plan";else{if(!g)continue;b="available"}const v=e?.bindingStatus||o.get(t)||Hn,y=s||i,$=Ln(t),w=e?.area||g?.areaId||y?.area||"",k=e?.space||y?.space||h[w]||"",x=e?.name||y?.name||g?.label||t,S=e?.model||y?.model||g?.model||"",M=!!g?.parentDeviceId&&p.has(`device:${g.parentDeviceId}`),D="readd"===b?"removed":"hidden"===b?s&&En(s)?d[t]||"automatic_hidden":"manual_hidden":"on_plan"===b?s?"visible_explicit":"visible_auto":d[t]||(M?"represented_by_parent":w&&k?"available":"no_bound_room"),C=!!e&&(!e.hidden||n),P="active"===v.kind,I=C&&(P||"ha_disabled"===v.kind&&n),T=[x,S,c[t],r[w],l[k],t,g?.sub].filter(Boolean).join(" ").toLocaleLowerCase();f.push({key:t,binding:t,category:b,status:v,reason:D,deviceId:e?.id,markerId:y?.id,name:x,icon:e?.icon||y?.icon||("entity"===$?"mdi:code-braces":"mdi:devices"),model:S,integration:c[t]||"",areaId:w,areaName:r[w]||"",spaceId:k,spaceName:l[k]||"",kind:$,isNew:!!e&&a.has(e.id),searchText:T,canFind:I,canEdit:!!e||!!s,canHide:"on_plan"===b&&P,canShow:"hidden"===b&&P,canAdd:("available"===b||"readd"===b)&&P})}const b=t=>"active"===t.status.kind?1:0;return f.sort((t,e)=>t.category.localeCompare(e.category)||Number(e.isNew)-Number(t.isNew)||b(t)-b(e)||t.name.localeCompare(e.name)||t.binding.localeCompare(e.binding))}({devices:this.host._devices,markers:this.host._markers,candidates:i,statuses:a,newDeviceIds:this.host._newIds,showHiddenOnPlan:this.host._showAll,areaNames:r,spaceNames:h,spaceByArea:c,integrationByBinding:d,reasonByBinding:u});return this.host._deviceInboxMemo={key:s,rows:_},_}_deviceForInboxRow(t){const e=t.deviceId?this.host._devices.find(e=>e.id===t.deviceId):null;if(e)return e;const s=t.markerId?this.host._markers.find(e=>e.id===t.markerId):null;return!s||s.removed?null:{id:s.id,name:t.name,model:t.model,area:t.areaId,space:t.spaceId||this.host._space,hidden:!0===s.hidden||"ha_disabled"===t.status.kind,userHidden:!0===s.hidden,bindingStatus:t.status,icon:t.icon,entities:"active"===t.status.kind?t.status.enabledEntityIds:[],allEntities:t.status.allEntityIds,primary:"active"===t.status.kind?t.status.enabledEntityIds[0]:void 0,marker:s,bindingKind:t.kind,bindingRef:t.binding.slice(t.binding.indexOf(":")+1),pdfs:s.pdfs||[]}}_openInboxMarker(t,e=!1){const s=this.host._deviceInbox;if(s){if(this.host._deviceInboxReturn={...s,anchor:t.key},this.host._deviceInbox=null,!e){const e=this._deviceForInboxRow(t);return void(e?this._openMarkerDialog(e):this._closeMarkerDialog())}this._openMarkerDialog(),this.host._markerDialog?this.host._markerDialog={...this.host._markerDialog,bindingMode:"ha",binding:t.binding,bindingOpen:!1,showEntities:"entity"===t.kind,name:""}:this._closeMarkerDialog()}}async _setInboxHidden(t,e){const s=this.host._deviceInbox,i=this.host._serverCfg;if(!s||!i||s.busy||"active"!==t.status.kind)return;const o=i.markers||[],a=o.find(e=>!e.removed&&e.binding===t.binding);if(!e&&!a)return;const n=a?.id||xs(t.binding,t.markerId,()=>`m_${Date.now().toString(36)}`),r=a?{...a,hidden:e}:{id:n,binding:t.binding,hidden:!0};i.markers=[...o.filter(e=>e.id!==n&&(e.binding!==t.binding||!0===e.removed)),r],this.host._deviceInbox={...s,busy:t.key,anchor:t.key};try{await this._saveConfigNow(),this.host._regSignature="",this.host._deviceInboxMemo=null,this.host._maybeRebuildDevices(),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("device_inbox.saved"))}catch(t){this.host._serverCfg===i&&(i.markers=o),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_findInboxDevice(t){if(!t.canFind)return;const e=this._deviceForInboxRow(t);if(!e)return;if(this.host._deviceInbox=null,e.space&&e.space!==this.host._space){if(!this.host._commitSpace(e.space))return;this.host._restoreZoom()}const s=()=>{const t=this.host._devices.find(t=>t.id===e.id)||e,s=this.host._pos(t);this.host._applyView(this.host._zoom,s.x,s.y),this.host._selId=t.id,this.host.requestUpdate(),window.setTimeout(()=>{this.host._selId===t.id&&(this.host._selId=null,this.host.requestUpdate())},1500)};requestAnimationFrame(()=>requestAnimationFrame(s))}_openMarkerDialog(t){if(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null,t&&this.host._ackNewDevice(t.id),this.host._norm)if(t){const e=t.marker,s=Object.prototype.hasOwnProperty.call(e||{},"is_light"),i=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),o=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),a=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),n=Object.prototype.hasOwnProperty.call(e||{},"value_source"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),l=Object.prototype.hasOwnProperty.call(e||{},"toggle_entity"),h=Ss(e?.glow_color),c=this.host._devicePresentation(t,!0).valueBadge,d=Ms(this.host._planHass,t,this.host._devices),p=Ds(this.host._planHass,t,d);this.host._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this.host._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Is(t.marker?.display),rippleColor:D(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):1.5,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Ps(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:e?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:l,originalToggleEntity:e?.toggle_entity,tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:Cs(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:s,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:i,originalLightEntity:e?.light_entity,glowMode:null!=h?.bri?"fixed":h?"color":"auto",glowColor:h?.c||this.host._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(h?.bri??1))),glowColorDrafted:!!h,glowBrightnessDrafted:null!=h?.bri,glowTouched:!1,originalHasGlowColor:o&&(!!h||null===e?.glow_color),originalGlowColor:h||(null===e?.glow_color?null:void 0),valueBadgeEnabled:a?!0===e?.value_badge?.enabled:!!c,valueBadgeSource:e?.value_badge?.source||c?.source||p,valueBadgePosition:e?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:a,originalValueBadge:e?.value_badge,valueSource:e?.value_source||null,valueSourceTouched:!1,originalHasValueSource:n,originalValueSource:e?.value_source,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this.host._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",roomTouched:!1,hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this.host._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:1.5,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",toggleEntity:"",toggleEntityTouched:!1,originalHasToggleEntity:!1,originalToggleEntity:void 0,tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this.host._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,valueSource:null,valueSourceTouched:!1,originalHasValueSource:!1,originalValueSource:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",roomTouched:!1,hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this.host._showToast(this.host._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ts)for(const[s,i]of Object.entries(this.host.hass.states))s.startsWith(e+".")&&this.host._planEntityAvailable(s)&&t.push({value:s,label:i?.attributes?.friendly_name||s,sub:this.host._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=Nn({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:!!this.host._markerDialog?.showEntities,currentBinding:this.host._markerDialog?.binding,currentDeviceId:this.host._markerDialog?.devId,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}}),e=(this.host._markerDialog?.bindingFilter||"").toLowerCase().trim(),s=e?t.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(e)):t;return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,s=this.host._t(t);if(s!==t)return s}return this.host._errText(t)}async _pickMarkerFiles(t){const e=t.target,s=e.files?[...e.files]:[];if(e.value="",!s.length||!this.host._markerDialog)return;const i=this.host._markerDialog.uploadId||this.host._markerDialog.devId||"new",o=[];for(const t of s)try{const e=new FormData;e.append("marker_id",i),e.append("file",t,t.name);const s=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await s.json().catch(()=>({}));if(!s.ok||a.error){const t={too_large:this.host._t("err.too_large",{mb:a.max_mb||50}),bad_ext:this.host._t("err.bad_ext"),unauthorized:this.host._t("err.unauthorized")};throw new Error(t[a.error]||a.error||"HTTP "+s.status)}o.push({name:a.name||t.name,url:a.url})}catch(e){this.host._showToast(this.host._t("toast.file_failed",{name:t.name,err:this.host._errText(e)}))}o.length&&this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:[...this.host._markerDialog.pdfs,...o]},this.host._showToast(this.host._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:this.host._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const s={c:D(t.glowColor,this.host._fillColors.glow_light.c)};"fixed"===t.glowMode&&(s.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=s}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}_markerToggleEntityFields(t){return(e={touched:t.toggleEntityTouched,originalHas:t.originalHasToggleEntity,original:t.originalToggleEntity,value:t.toggleEntity}).touched?e.value?{toggle_entity:e.value}:{}:e.originalHas?{toggle_entity:e.original??null}:{};var e}async _saveMarker(){const t=this.host._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return;if("virtual"===t.binding&&!t.name.trim())return void this.host._showToast(this.host._t("toast.virtual_name_required"));if("run"===e&&!t.tapTarget)return void this.host._showToast(this.host._t("toast.run_target_required"));if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)return void this.host._showToast(this.host._t("toast.value_badge_source_required"));if("ha"===t.bindingMode){const e=this.host._bindingStatus(t.binding),s=t.devId?this.host._markers.find(e=>e.id===t.devId):null,i=!s||s.binding!==t.binding;if("active"!==e.kind&&i)return void this.host._showToast(this.host._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===s?.hidden&&!t.hideFromPlan)return this.host._markerDialog={...t,hideFromPlan:!0},void this.host._showToast(this.host._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const s=this.host._serverCfg;if(!s)return;const i=this.host._cfgRev,o=_(s);let a=JSON.parse(JSON.stringify(s));const n=a.markers||[],r=xs(t.binding,t.devId,()=>"v_"+Date.now().toString(36)),l=t.devId,h=l?this.host._devices.find(t=>t.id===l):null,c=l?n.find(t=>t.id===l):null,d=!!c&&("string"==typeof c.area&&c.area.length>0||null===c.area&&!!c.space&&!!c.room_id),p="virtual"===t.binding||t.roomTouched||d,g="virtual"===t.binding||t.roomTouched?zs(t.room):null;let f=d&&!t.roomTouched?c?.space||null:g?.space||null;const b=d&&!t.roomTouched?c?.area||null:g?.area||null,v=d&&!t.roomTouched?c?.room_id||null:g?.roomId||null,y=f||h?.space||null,$=y?this.host._spaceModelById(y):this.host._spaceModel();if(!$)return;const w=$.id;"virtual"!==t.binding||f||(f=w),this.host._markerDialog={...t,busy:!0};let k=null,x=!1;try{const c="virtual"===t.binding?[]:n.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),d=c.length>0,m=Cs(t.binding,t.controls,this.host._bindingEntities(t.binding)),g=n.find(t=>t.id===r||t.id===l)?.vacuum||null,y={id:r,vacuum:g,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:m.length?m:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Oh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};p&&(y.space=f,y.area=b,y.room_id=v);const S=h?.marker?.room_id??null,M=t.roomTouched&&null!=h&&(h.space!==f||h.area!==b||S!==v);let D=!1;const C=t.uploadId||l;if(C&&C!==r&&y.pdfs?.length)try{const t=await this.host.hass.callWS({type:"houseplan/files/migrate",from_id:C,to_id:r}),e=t?.mapping||{};y.pdfs=Rs(y.pdfs,C,r,e),D=Object.keys(e).length>0}catch(t){this.host._showToast(this.host._t("toast.files_migrate_failed",{err:this.host._errText(t)}))}a.markers=n,l&&l!==r&&(a.markers=As(a.markers,l,r)),l&&l!==r&&"derived_marker_state"===y.value_badge?.source?.kind&&y.value_badge.source.ref===`marker:${l}`&&(y.value_badge.source={kind:"derived_marker_state",ref:`marker:${r}`}),l&&l!==r&&"derived_marker_state"===y.value_source?.kind&&y.value_source.ref===`marker:${l}`&&(y.value_source={kind:"derived_marker_state",ref:`marker:${r}`}),a.markers=a.markers.filter(t=>t.id!==r&&t.id!==l&&("virtual"===y.binding||t.binding!==y.binding)),a.markers.push(y);const P=new Set(c);l&&l!==r&&P.add(l),P.delete(r),P.size&&a.settings?.marker_area_snapshot&&(a.settings={...a.settings,marker_area_snapshot:Fs(a.settings.marker_area_snapshot,P)});let I=null;const T=l?this.host._layout[l]:null,z=T?{s:T.s||h?.space||this.host._space,x:T.x,y:T.y}:l&&h&&this.host._defPos[l]?this.host._normPos(h.space,this.host._defPos[l].x,this.host._defPos[l].y):null;if(!d&&z&&z.s===w)r===l&&this.host._layout[r]&&!M||(I={s:z.s,x:z.x,y:z.y});else if(d||!this.host._layout[r]||M){let t=$.vb[0]+$.vb[2]/2,e=$.vb[1]+$.vb[3]/2;const s=v?$.rooms.find(t=>t.id===v):b?$.rooms.find(t=>t.area===b):void 0;s&&([t,e]=this.host._roomCenter(s)),I=this.host._normPos(w,t,e)}if(this.host._serverCfg!==s||this.host._cfgRev!==i||_(s)!==o)return this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),void this.host._showToast(this.host._t("toast.conflict"));if(a=this._prepareConfigCandidate(a),k=u(s,a,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=a,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.cancel(),await this._saveConfigNow(k),x=!0,I){const t=Ns(I);this.host._layout={...this.host._layout,[r]:t},this.host._noteLayoutRev(await this.host.hass.callWS({type:"houseplan/layout/update",device_id:r,pos:t}))}const R=new Set(c);l&&l!==r&&R.add(l),R.delete(r);for(const t of R)delete this.host._layout[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{});D&&C&&await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:C}).catch(()=>{}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_saved"))}catch(t){!x&&k&&(m(this.host,k,_),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteMarker(){const t=this.host._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this.host._devices.find(e=>e.id===t.devId):null,s=this.host._markers.find(e=>e.id===t.devId);if(!e&&!s)return;const i=e?"virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"":s.binding;if(!i)return;const o=t.name||this.host._t("device.fallback"),a=t.devId,n=await this.host._confirmDanger({key:"remove-marker",kind:"destructive",title:this.host._t("confirm.remove_marker_title"),message:this.host._t("confirm.remove_marker_body"),objectName:o,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._markerDialog;if(!n||!r||r.busy||r.devId!==a)return;const l=this.host._devices.find(t=>t.id===a),h=this.host._markers.find(t=>t.id===a);if(!l&&!h)return;const c=this.host._serverCfg;if(!c)return;c.markers=c.markers||[];const d=c.markers,p=l?"virtual"===l.bindingKind?"virtual":l.bindingKind&&l.bindingRef?`${l.bindingKind}:${l.bindingRef}`:"":h.binding;if(!p||p!==i)return;const u=Hs(c.markers,a,p,"virtual"===p);c.markers=Ls(u.markers,u.cleanupIds);const _=u.cleanupIds;_.size&&c.settings?.marker_area_snapshot&&(c.settings={...c.settings,marker_area_snapshot:Fs(c.settings.marker_area_snapshot,_)}),this.host._markerDialog={...r,busy:!0};try{await this._saveConfigNow();for(const t of _){delete this.host._layout[t],delete this.host._defPos[t],this.host._dirtyPos.delete(t),this.host._sentPos.delete(t);const e=this.host._activityRt.get(t);e&&clearTimeout(e.timer),this.host._activityRt.delete(t),this.host._vacRt.delete(t),delete this.host._vacSrvTrails[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this.host._deviceInboxReturn&&(this.host._deviceInboxReturn={...this.host._deviceInboxReturn,tab:"readd",anchor:p}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._infoCard?.id===a&&this.host._closeInfoCard(),this.host._selId===a&&(this.host._selId=null),this.host._drag?.id===a&&(this.host._drag=null),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_removed"))}catch(t){this.host._serverCfg===c&&(c.markers=d),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_openSpaceDialog(t,e){if(this.host._serverStorage&&this.host._serverCfg)if("edit"===t){const s=this.host._serverCfg.spaces.find(t=>t.id===e);if(!s)return;const i=Es(s),o=s.settings?.custom_fill&&"object"==typeof s.settings.custom_fill?Os(s.settings.custom_fill):null,a="none"===i.fill?{...o||Bs,a:0}:o;this.host._spaceDialog={mode:t,spaceId:e,title:s.title,planUrl:s.plan_url||null,planFile:null,source:s.plan_url?"file":"draw",showBorders:i.showBorders,showNames:i.showNames,zeroWallStyle:js(s),displayTouched:!0,hideDecor:i.hideDecor,hideOpenings:i.hideOpenings,roomColor:i.color,roomOpacity:i.opacity,fillMode:"none"===i.fill?"custom":i.fill,customFill:a,glowEnabled:i.glow,bgColor:i.bgColor,bgMode:"static"===s.settings?.bg_mode||"daynight"===s.settings?.bg_mode?s.settings.bg_mode:null,northDeg:ne({},s.settings),sunRays:"boolean"==typeof s.settings?.sun_rays?s.settings.sun_rays:null,tempMin:i.tempMin,tempMax:i.tempMax,showLqi:i.showLqi??this.host._config?.show_signal??!0,cardFontScale:i.cardFontScale,labelTemp:i.labelTemp,labelHum:i.labelHum,labelLqi:i.labelLqi,labelLight:i.labelLight,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,cellCmInput:Ws(Number(s.cell_cm)>0?Number(s.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1}}else this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...ro(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:Ks,roomOpacity:Us,fillMode:"custom",customFill:{...Bs,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:qs,tempMax:Gs,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Vs(this.host._imperial),cellCmInput:Ws(Vs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1};else this.host._showToast(this.host._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,s=e.files?.[0];if(!s||!this.host._spaceDialog)return;e.value="",this._decorAssetGuardReplace=null;const i=await no(s);if("reject"===i.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===i.kind)return void(this.host._backdropGuard=i.state);const o=await lo(s,i.ext,s.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:o})}_renderBackdropGuard(){const t=this._decorAssetGuardReplace;return null!==t?ho(this.host,()=>{},()=>{this.host._backdropGuard=null,this._decorAssetGuardReplace=null},this.host.hass,(e,s)=>this._uploadDecorImage(e,s,t),(this.host._backdropGuard?.file.size||0)<=2097152)??d:ho(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??d}_useServerPlan(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this.host._display(t);if(e){const s=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:0),s.onerror=()=>t(0),s.src=e}),i=this.host._spaceDialog;return i&&i.planUrl===t&&Number.isFinite(s)&&s>0?(this.host._spaceDialog={...i,savedAspect:s},s):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const e=this.host._spaceDialog,s=e?.saved?.find(e=>e.name===t);if(!e||!s||s.used_by.length||s.url===e.planUrl)return;const i=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),o=this.host._spaceDialog,a=o?.saved?.find(e=>e.name===t);if(i&&o&&a&&a.url===s.url&&a.modified===s.modified&&!a.used_by.length&&a.url!==o.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this.host._spaceDialog;e?.saved&&(this.host._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
    ${this.host._t("space.loading")}
    `;const e=t.saved||[];if(!e.length)return p`
    ${this.host._t("space.no_saved")}
    `;return p`
    + ${e.map(e=>{return p` +
    + +
    + ${e.name} + ${s=e.size,s>=1048576?(s/1048576).toFixed(1)+" MB":Math.round(s/1024)+" KB"}${e.used_by.length?" · "+this.host._t("space.used_by",{list:e.used_by.join(", ")}):""} +
    + + +
    `;var s})} +
    `}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const e="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const s="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let i=null;if("file"===t.source&&t.planFile){i={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:s,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!i&&"file"===t.source&&t.planUrl&&!o&&this.host._aspectJob&&(o=await this.host._aspectJob||null);const a=this.host._serverCfg;let n;if("create"===t.mode)n=co(s,t.title.trim()),a.spaces.push(n);else{if(n=a.spaces.find(t=>t.id===s),!n)throw new Error("space "+s+" is gone from the config");n.title=t.title.trim()}i?(n.plan_url=i.url,n.plan_aspect=i.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==n.plan_url&&(n.plan_url=t.planUrl,n.plan_aspect=o),"draw"===t.source&&(n.plan_url=null,n.plan_aspect=null,delete n.plan_x,delete n.plan_y,delete n.plan_scale,delete n.plan_scale_x,delete n.plan_scale_y,delete n.plan_angle),n.settings={...n.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):qs,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):Gs,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},n.zero_wall_style=t.zeroWallStyle,n.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(Eh,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(n.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length?this._openNextImport():e||this.host._importTotal>0?(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._showToast(this.host._t(e&&!this.host._importTotal?"toast.space_added_onboard":"import.done"))):(this.host._showToast("create"===t.mode?this.host._t("toast.space_added"):this.host._t("toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?this._setMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._saveNav())))}catch(t){"conflict"!==t?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this.host._serverCfg;if(!e)return;const s=e.spaces.find(e=>e.id===t.spaceId),i=po(e,this.host._layout||{},t.spaceId||""),o=1===e.spaces.length&&e.spaces[0]?.id===t.spaceId;if(i.count&&!o)return void(this.host._spaceDialog={...t,deleteBlockers:i.count});if(!s)return;const a=t.spaceId,n=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:uo(this.host._t("confirm.delete_space_body"),this.host._t("confirm.delete_space_vac_routes"),i.routeCount),objectName:s.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._spaceDialog,l=this.host._serverCfg;if(!n||!r||"edit"!==r.mode||r.busy||r.spaceId!==a||!l)return;if(!l.spaces.find(t=>t.id===a))return;const h=po(l,this.host._layout||{},a),c=1===l.spaces.length&&l.spaces[0]?.id===a;if(!h.count||c){this.host._spaceDialog={...r,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:a,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(e,s),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===a&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=po(e,this.host._layout||{},a),s=1===e.spaces.length&&e.spaces[0]?.id===a;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...r,deleteBlockers:h.count}}async _saveConfigNow(t=null){this.host._cfgEpoch++;try{await this._writeConfig(t)}catch(t){throw t?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===t?.code&&await this.host._reloadConfigOnly(),t}}_startImport(){const t=this.host._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,e.length?(this.host._importQueue=e,this.host._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();void 0!==t&&(this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...ro(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:Ks,roomOpacity:Us,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:qs,tempMax:Gs,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Vs(this.host._imperial),cellCmInput:Ws(Vs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._showToast(this.host._t("import.done")))}_renderImportDialog(){const t=this.host._importDialog,e=t.floors.filter(t=>t.checked).length;return p`this.host._importDialog=null}> +
    +
    ${this.host._t("import.hint")}
    + ${t.floors.map((e,s)=>p``)} +
    +
    + + + +
    +
    `}_supportPatch(t,e){const s=this.host._supportDialog;if(!s||s.draftId!==t)return null;const i={...s,...e};return this.host._supportDialog=i,i}async _focusSupport(t){await this.host.updateComplete,this.host.renderRoot.querySelector(t)?.focus({preventScroll:!0})}_updateSupportDraft(t,e){const s=this.host._supportDialog;if(!s||"building"===s.status||"sending"===s.status||"success"===s.status)return;const i={...s,[t]:e},o=_h({...i,attach:!1,preview:null});"message_required"===o&&"validation.message_required"===s.errorCode||"message_too_long"===o||"contact_too_long"===o?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:`validation.${o}`}):i.attach&&i.preview&&i.preview.expiresAt<=Date.now()?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:"support_preview_expired"}):this._supportPatch(s.draftId,{[t]:e,status:i.attach&&i.preview?"ready":"idle",errorCode:""})}async _discardSupportPreview(t){if(t)try{await this.host.hass.callWS({type:"houseplan/support/preview/discard",token:t})}catch{}}async _closeSupportDialog(){const t=this.host._supportDialog;if(t){if("building"===t.status||"sending"===t.status){const t=l(this.host.hass,this.host._config?.language);if(!await this.host._confirmDanger({key:"close-support-busy",kind:"warning",title:ko(t,"support.close_busy_title"),message:ko(t,"support.close_busy_body"),confirmLabel:this.host._t("btn.close"),cancelLabel:this.host._t("btn.cancel")}))return await this.host.updateComplete,void this.host.renderRoot.querySelector("#support-dialog")?.rejectClose()}clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this.host._supportDialog=null,t.preview?.token&&"success"!==t.status&&this._discardSupportPreview(t.preview.token)}}_scheduleSupportExpiry(t,e){clearTimeout(this._supportExpiryTimer);const s=Math.max(0,e.expiresAt-Date.now());this._supportExpiryTimer=window.setTimeout(()=>{const s=this.host._supportDialog;s&&s.draftId===t&&s.preview?.token===e.token&&s.attach&&"success"!==s.status&&"sending"!==s.status&&this._supportPatch(t,{status:"error",errorCode:"support_preview_expired"})},Math.min(s+20,2147483647))}_supportFacts(){const t=this.host._haRegistry;return gh({userAgent:globalThis.navigator?.userAgent||"",language:l(this.host.hass,this.host._config?.language),registryAccess:t.access,registryLastSuccess:t.lastSuccess})}_supportPreviewRequestIsCurrent(t,e){const s=this.host._supportDialog;return e===this._supportPreviewGeneration&&s?.draftId===t&&s.attach}async _buildSupportPreview(t){const e=this.host._supportDialog;if(!e||e.draftId!==t||!e.attach||!ch(this.host._haSupportApi))return;const s=++this._supportPreviewGeneration;this._supportPatch(t,{status:"building",errorCode:""});let i="";try{const e=await this.host.hass.callWS({type:"houseplan/support/preview",card_version:Fh,...this._supportFacts(),draft_id:t}),o=e&&"object"==typeof e?e:{},a=Date.now(),n="string"==typeof o.text?o.text:"",r=Number(o.size),l=Number(o.expires_in),h=Number(o.spaces),c=String(o.token||"");/^[0-9a-f]{48}$/.test(c)&&(i=c);const d=String(o.sha256||""),p=Number(o.version),u=String(o.format||""),_=(new TextEncoder).encode(n).byteLength;if(!/^[0-9a-f]{48}$/.test(c)||!/^[0-9a-f]{64}$/.test(d)||"houseplan-support-package"!==u||1!==p||!Number.isInteger(r)||r!==_||!Number.isInteger(h)||h<0||!Number.isFinite(l)||l<=0||l>600||!n.endsWith("\n"))throw{code:"support_rejected"};const m={token:c,expiresAt:a+1e3*l,size:r,sha256:d,spaces:h,format:u,version:p,text:n,preparedAt:a};if(!this._supportPreviewRequestIsCurrent(t,s))return i="",void this._discardSupportPreview(c);if(!this._supportPatch(t,{status:"ready",preview:m,errorCode:"",rawOpen:!1}))return i="",void this._discardSupportPreview(c);i="",this._scheduleSupportExpiry(t,m)}catch(e){if(i){const t=i;i="",this._discardSupportPreview(t)}if(!this._supportPreviewRequestIsCurrent(t,s))return;if(!this._supportPatch(t,{status:"error",errorCode:fh(e)}))return;this._focusSupport("#support-error")}}async _setSupportAttachment(t){const e=this.host._supportDialog;if(!e||"sending"===e.status||"success"===e.status)return;const s=e.preview?.token||"";if(!t)return this._supportPreviewGeneration+=1,clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPatch(e.draftId,{attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:""}),void(s&&this._discardSupportPreview(s));this._supportPatch(e.draftId,{attach:!0,errorCode:""}),await this._buildSupportPreview(e.draftId)}async _refreshSupportPreview(){const t=this.host._supportDialog;t&&t.attach&&"building"!==t.status&&"sending"!==t.status&&await this._buildSupportPreview(t.draftId)}_downloadSupportPreview(){const t=this.host._supportDialog?.preview;if(!t)return;const e=new Blob([t.text],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(e),i=document.createElement("a");i.href=s,i.download=`houseplan-support-${t.sha256.slice(0,12)}.json`,i.style.display="none",document.body.append(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0)}async _copySupportText(t,e){const s=l(this.host.hass,this.host._config?.language);try{await navigator.clipboard.writeText(t),this.host._showToast(ko(s,e))}catch{this.host._showToast(ko(s,"support.copy_failed"))}}async _submitSupport(){const t=this.host._supportDialog;if(!t||!ch(this.host._haSupportApi))return;const e=_h(t);if(e)return this._supportPatch(t.draftId,{status:"error",errorCode:`validation.${e}`}),void this._focusSupport("#support-message");if(!mh(t))return;const s=ph(t),i=this._supportPatch(t.draftId,{status:"sending",errorCode:"",idempotencyKey:s.idempotencyKey,submissionFingerprint:s.fingerprint});if(i)try{const e=await this.host.hass.callWS({type:"houseplan/support/submit",message:i.message.trim(),contact:i.contact.trim(),...i.attach&&i.preview?{preview_token:i.preview.token}:{},idempotency_key:i.idempotencyKey}),s=e&&"object"==typeof e&&"report_id"in e?String(e.report_id||""):"";if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(s))throw{code:"support_unavailable"};if(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,!this._supportPatch(t.draftId,{status:"success",reportId:s,errorCode:""}))return;this._focusSupport("#support-receipt")}catch(e){if(!this._supportPatch(t.draftId,{status:"error",errorCode:fh(e)}))return;this._focusSupport("#support-error")}}_supportErrorText(t){const e=l(this.host.hass,this.host._config?.language);if(t.errorCode.startsWith("validation.")){return ko(e,`support.validation.${t.errorCode.slice(11)}`)}return ko(e,`support.error.${t.errorCode||"support_unavailable"}`)}_supportMessageKeydown(t){"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this._submitSupport())}_renderSupportDialog(){const t=this.host._supportDialog,e=l(this.host.hass,this.host._config?.language),s="ru"===e?"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.ru.md":"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.md",i=ch(this.host._haSupportApi),o=(t,s)=>ko(e,t,s),a="building"===t.status||"sending"===t.status,n=_h(t),r=t.preview?Math.max(0,Math.floor((Date.now()-t.preview.preparedAt)/6e4)):0,h="error"===t.status&&!t.errorCode.startsWith("validation."),c="validation.contact_too_long"===t.errorCode,u="validation.message_required"===t.errorCode||"validation.message_too_long"===t.errorCode;return p`{this._closeSupportDialog()}}> +
    +
    +

    ${o("support.about_group")}

    +
    ${this.host._t("gs.about_version",{v:Fh})}
    + +
    +
    +

    ${o("support.guide_group")}

    + + ${o("support.guide")} +
    + ${i?p` +
    +

    ${o("support.form_group")}

    + + this._updateSupportDraft("contact",t.target.value)} /> + + + +

    ${o("support.attach_hint")}

    + ${t.attach?p`

    + + ${o("support.geometry_warning")} +

    `:d} + ${"building"===t.status?p`
    + ${o("support.building")} +
    `:d} + ${t.attach&&t.preview?p` +
    +
    ${o("support.preview_summary",{version:t.preview.version,spaces:t.preview.spaces,size:(_=t.preview.size,(Math.max(0,_)/1024).toFixed(_<10240?1:0))})}
    +
    SHA-256${t.preview.sha256}
    +
    ${o("support.prepared",{n:r})}
    +
    this._supportPatch(t.draftId,{rawOpen:e.currentTarget.open})}> + ${o("support.show_data")} + ${t.rawOpen?p``:d} +
    +
    + + +
    +
    `:d} +

    ${o("support.privacy")}

    + ${"error"===t.status?p` + `:d} + ${"error"===t.status&&t.attach&&!t.preview?p` +
    + +
    `:d} + ${h?p` +
    + ${o("support.manual_recovery")} +
    + + ${t.preview?p``:d} + Telegram + GitHub +
    +
    `:d} + ${"success"===t.status?p` +
    + ${o("support.success",{id:t.reportId})} + +
    `:d} +
    `:p` +
    + + ${o("support.update_required")} +
    `} +
    +
    + + + ${i&&"success"!==t.status?p` + `:d} +
    +
    `;var _}_preflightDiagnostics(t,e){const s=new Map((e?.spaces||[]).map(t=>[String(t?.id||""),t]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:Fh,checkedAt:(new Date).toISOString(),preflightFingerprint:t.fingerprint,failures:t.failures.map(t=>({spaceId:t.spaceId,displayName:t.displayName,reason:t.reason,detail:t.detail??null,spaceGeometryFingerprint:s.has(t.spaceId)?$e(s.get(t.spaceId)):null}))}}_reportPreflightFailure(t,e){t.ok||t.fingerprint===this.host._reportedPreflightFingerprint||(this.host._reportedPreflightFingerprint=t.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(t,e)))}_preflightVersionsDiffer(){const t=this.host._haIntegrationVersion;return"string"==typeof t&&t.length>0&&t!==Fh}async _copyPreflightDiagnostics(){const t=this.host._alignDialog?.preflight;if(!t||t.ok)return;const e=JSON.stringify(this._preflightDiagnostics(t,this.host._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(e),this.host._preflightClipboardFallback=null,this.host._showToast(this.host._t("gs.preflight_copied"))}catch{this.host._preflightClipboardFallback=e}}_checkOptimizeGeometryImpl(t){return Js(t,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)})})}_checkSpacePhysicalGeometryImpl(t,e,s){return Ys(t,e,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)}),captureWallGeometry:s?(t,e)=>s(e):void 0})}_optimizeReferenceContext(t){const e=this.host._haRegistry,s=this.host._fullRegistryHass,i={},o=(...t)=>{for(const e of t){const t=String(e||"").trim();if(t)return t}return""};for(const[t,s]of Object.entries(e.devices||{}))i[t]=o(s?.name_by_user,s?.name,s?.model);for(const[t,s]of Object.entries(e.entities||{}))i[`lg_${t}`]=o(this.host.hass?.states?.[t]?.attributes?.friendly_name,s?.name,s?.original_name);for(const t of this.host._devices)i[t.id]=o(t.name,i[t.id]);for(const t of this.host._serverCfg?.markers||[]){const e=String(t.binding||"").indexOf(":"),a=e>0?t.binding.slice(0,e):"",n=e>0?t.binding.slice(e+1):"",r="device"===a?s?.devices?.[n]:"entity"===a?s?.entities?.[n]:null;i[t.id]=o(t.name,"device"===a?r?.name_by_user:null,r?.name,"entity"===a?this.host.hass?.states?.[n]?.attributes?.friendly_name:null,i[t.id])}return{effectiveAreaByMarker:Object.fromEntries(this.host._devices.filter(t=>!t.virtual&&!!t.area).map(t=>[t.id,t.area])),ownerRoster:{authoritative:e.authoritative,deviceIds:Object.keys(e.devices||{}),entityIds:[...new Set([...Object.keys(e.entities||{}),...Object.keys(this.host.hass?.states||{})])],names:i},removeLiveMissingPositions:t}}_previewAlignDialog(t){if(!this.host._norm||!this.host._serverCfg)return;const e=this.host._serverCfg.spaces||[];let s;try{s=nh(this.host._serverCfg,this.host._layout||{},this._optimizeReferenceContext(t))}catch(t){return void this._showWallModelMigrationBlocked(t)}const i=s.changed?this.host._checkOptimizeGeometry(s.config):null;i&&this._reportPreflightFailure(i,s.config);const o=Math.ceil(10*s.report.maxShiftCm)/10,a=e.find(t=>null!=t?.id&&String(t.id)===s.report.maxSpace),n=e.length>1&&a?String(a.title||a.id):"";this.host._preflightClipboardFallback=null,this.host._alignDialog={report:s.report,config:s.config,layout:s.layout,cm:o,where:n,preflight:i,changed:s.changed,busy:!1,removeLiveMissingPositions:t}}async _runAlignToGrid(){let t=this.host._alignDialog;if(!t||t.busy||!this.host._serverCfg||!t.changed||!t.preflight?.ok)return;const e=_(t.config);if(t.preflight.fingerprint!==e){const e=this.host._checkOptimizeGeometry(t.config);if(this._reportPreflightFailure(e,t.config),t={...t,preflight:e},this.host._alignDialog=t,!e.ok)return}this._clearGeometryGesture(),this.host._alignDialog={...t,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain;const e=await this.host.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});this.host._serverCfg=t.config,this.host._layout=t.layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._cfgRev=e?.config_rev??this.host._cfgRev+1,this.host._layoutRev=e?.layout_rev??this.host._layoutRev+1,this.host._canOptimizeUndo=!!e?.can_undo,this.host._undoKind=e?.can_undo?"optimize":null,this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host._alignDialog=null,this.host._preflightClipboardFallback=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.coordsCanonicalized+t.report.latticeCoordinatesCanonicalized+t.report.wallsMerged+t.report.spansMerged+t.report.partitionsMerged+t.report.partitionsReconciled+t.report.openingsRehosted+t.report.wallsStraightened),r:String(t.report.spaceRefsRemapped+t.report.roomRefsRemapped+t.report.positionsRemapped+t.report.markersDetached+t.report.orphanRoomLabelsRemoved+t.report.orphanDevicePositionsRemoved+t.report.orphanGroupPositionsRemoved)}))}catch(t){if(this.host._alignDialog&&(this.host._alignDialog={...this.host._alignDialog,busy:!1}),"wall_model_client_outdated"===t?.code)return void this.host._showToast(this.host._t("toast.wall_model_client_outdated"));"conflict"===t?.code&&await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _undoPlanOptimization(){if(!this.host._canOptimizeUndo||this.host._optimizeUndoBusy)return;const t=this.host._undoKind;this._clearGeometryGesture(),this.host._optimizeUndoBusy=!0,this.host.requestUpdate();try{await this.host.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});const[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._serverCfg=e?.config||this.host._serverCfg,this.host._cfgRev=e?.rev??this.host._cfgRev,this.host._layout=s?.layout||this.host._layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._layoutRev=s?.rev??this.host._layoutRev,this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host.requestUpdate(),this.host._showToast(this.host._t("import"===t?"backup.import_undone":"gs.optimize_undone"))}catch(t){this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}finally{this.host._optimizeUndoBusy=!1,this.host.requestUpdate()}}async _runBackupExport(){const t=this.host._backupExportDialog;if(t&&!t.busy){this.host._backupExportDialog={...t,busy:!0,error:""};try{const e=await this.host.hass.callWS({type:"houseplan/export/create",kind:t.kind,space_id:"space"===t.kind?this.host._space:void 0,..."space"===t.kind&&t.planOnly?{plan_only:!0}:{},card_version:Fh}),s=new Blob([JSON.stringify(e.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),i=URL.createObjectURL(s),o=document.createElement("a");o.href=i,o.download=e.filename||`houseplan-${t.kind}.json`,o.style.display="none",document.body.append(o),o.click(),o.remove(),window.setTimeout(()=>URL.revokeObjectURL(i),0),this.host._backupExportDialog=null,this.host._showToast(this.host._t("backup.export_done"))}catch(t){this.host._backupExportDialog&&(this.host._backupExportDialog={...this.host._backupExportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _pickBackupImport(t){const e=t.target,s=e.files?.[0];if(e.value="",s){this.host._settingsDialog=null,this.host._backupImportDialog={filename:s.name,size:s.size,token:"",preview:null,expectedConfigRev:this.host._cfgRev,expectedLayoutRev:this.host._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain,this.host._persistLayout.pending()&&this.host._persistLayout.flush();const t="/api/houseplan/import/preview?duplicate_policy=skip",e={method:"POST",body:s,headers:{"content-type":"application/json"}},i=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth(t,e):await fetch(t,{...e,headers:{...e.headers,...this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error)throw Object.assign(new Error(o.message||o.error||`HTTP ${i.status}`),{code:o.error});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,token:o.token,preview:o.preview,expectedConfigRev:o.expected_config_rev,expectedLayoutRev:o.expected_layout_rev,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _setBackupDuplicatePolicy(t){const e=this.host._backupImportDialog;if(e&&!e.busy&&e.token&&t!==e.duplicatePolicy){this.host._backupImportDialog={...e,duplicatePolicy:t,busy:!0,error:""};try{const s=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:t});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,preview:{...e.preview,...s.preview},expectedConfigRev:s.expected_config_rev,expectedLayoutRev:s.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,duplicatePolicy:e.duplicatePolicy,preview:e.preview,confirmMissing:e.confirmMissing,busy:!1,error:this._backupErrorText(t)})}}}async _applyBackupImport(){const t=this.host._backupImportDialog;if(!t||t.busy||!t.token||!t.preview)return;if(t.preview.confirmation_required&&!t.confirmMissing)return;this.host._backupImportDialog={...t,busy:!0,error:""};const e=this.host._space;try{const s=await this.host.hass.callWS({type:"houseplan/import/apply",token:t.token,expected_config_rev:t.expectedConfigRev,expected_layout_rev:t.expectedLayoutRev,duplicate_policy:t.duplicatePolicy,confirm_missing_content:t.confirmMissing}),[i,o]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(i,o),this.host._geometryHistory.clear(),this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._defPos={},this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._visibleDeviceSnapshot=null,this.host._candidateDeviceSnapshot=null,this.host._stagedDeviceSnapshotToken=-1,this.host._capturedSnapshotConfigEpoch=-1,this.host._regSignature="",this.host._signer.invalidate(this.host.hass),this.host._resign(),this.host._maybeRebuildDevices();const a=this.host._serverCfg?.spaces||[],n="space"===s.kind&&s.space_id?s.space_id:a.some(t=>t.id===e)?e:a[0]?.id||this.host._space;this.host._hasFixedFloor?this.host._adoptInitialSpace(this.host._model,!0):this.host._commitSpace(n),this.host._backupImportDialog=null,this.host._cacheSnapshot(),this.host.requestUpdate();const r="space"===s.kind?t.preview.counts:s.counts;this.host._showToast(this.host._t("space"===s.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(s.repaired_target_refs||0)}))}catch(t){if("conflict"===t?.code&&this.host._backupImportDialog?.token)try{const t=this.host._backupImportDialog,e=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:t.duplicatePolicy});return void(this.host._backupImportDialog={...t,preview:{...t.preview,...e.preview},expectedConfigRev:e.expected_config_rev,expectedLayoutRev:e.expected_layout_rev,confirmMissing:!1,busy:!1,error:this.host._t("backup.revalidated")})}catch(e){t=e}this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}_renderBackupExportDialog(){const t=this.host._backupExportDialog,e=(this.host._serverCfg?.spaces||[]).find(t=>t.id===this.host._space);return p`this.host._backupExportDialog=null}> +
    +
    ${this.host._t("backup.export_hint")}
    + + + ${"space"===t.kind&&e?p``:d} +
    ${this.host._t("backup.privacy_warning")}
    + ${t.error?p``:d} +
    +
    + + + +
    +
    `}_renderBackupImportDialog(){const t=this.host._backupImportDialog,e=t.preview,s=(e?.content||[]).filter(t=>"decor_asset"===t.kind),i=new Set(s.map(t=>t.url)).size,o=s.filter(t=>"missing_preserved"===t.state),a=new Set(o.map(t=>t.url)).size,n=e?.counts||{},r=e?.reference_report||{},l=t=>Object.values(t||{}).reduce((t,e)=>t+(Number(e)||0),0),h=[["incoming_remapped",l(r.remapped?.incoming)],["target_repaired",l(r.remapped?.target)],["preserved_unresolved",l(r.preservedUnresolved)],["collisions",l(r.collisions)],["dropped_links",l(r.droppedIncomingLinks)],["bounded_lineages",Number(r.boundedLineages)||0]].filter(([,t])=>t>0);return p`this.host._backupImportDialog=null}> +
    +
    ${t.filename}${(t.size/1024).toFixed(1)} KB
    + ${t.busy&&!e?p`
    ${this.host._t("backup.reading")}
    `:d} + ${t.error?p``:d} + ${e?p` +
    + ${this.host._t("full"===e.kind?"backup.full":"backup.current_space")} + ${e.plan_only?p`${this.host._t("backup.plan_only_preview")}`:d} + ${this.host._t("same"===e.source?"backup.same_source":"backup.foreign_source")} + ${this.host._t("backup.created",{value:e.created_at||"—"})} + ${this.host._t("backup.versions",{card:e.card_version||"—",integration:e.integration_version||"—",model:String(e.model_version??"—")})} +
    +
    + ${this.host._t("backup.count_spaces",{n:"full"===e.kind?`${e.current_counts?.spaces||0} → ${n.spaces||0}`:String(n.spaces||0)})} + ${this.host._t("backup.count_rooms",{n:"full"===e.kind?`${e.current_counts?.rooms||0} → ${n.rooms||0}`:String(n.rooms||0)})} + ${this.host._t("backup.count_walls",{n:"full"===e.kind?`${e.current_counts?.walls||0} → ${n.walls||0}`:String(n.walls||0)})} + ${this.host._t("backup.count_openings",{n:"full"===e.kind?`${e.current_counts?.openings||0} → ${n.openings||0}`:String(n.openings||0)})} + ${this.host._t("backup.count_decor",{n:"full"===e.kind?`${e.current_counts?.decor||0} → ${n.decor||0}`:String(n.decor||0)})} + ${this.host._t("backup.count_markers",{n:"full"===e.kind?`${e.current_counts?.markers||0} → ${n.markers||0}`:String(n.markers||0)})} + ${this.host._t("backup.count_layout",{n:"full"===e.kind?`${e.current_counts?.layout||0} → ${n.layout||0}`:String(n.layout||0)})} +
    + ${e.bindings?p`
    ${this.host._t("backup.bindings",{device:String(e.bindings.device||0),entity:String(e.bindings.entity||0),virtual:String(e.bindings.virtual||0),legacy:String(e.legacy_positions||0)})}
    ${this.host._t("backup.binding_status",{active:String(e.bindings.active||0),disabled:String(e.bindings.disabled||0),missing:String(e.bindings.missing||0)})}
    `:d} + ${e.missing_areas?.length?p`
    ${this.host._t("backup.missing_areas",{areas:e.missing_areas.join(", ")})}
    `:d} + ${e.dropped_marker_links?p`
    ${this.host._t("backup.dropped_marker_links",{n:String(e.dropped_marker_links)})}
    `:d} + ${e.repaired_target_refs?p`
    ${this.host._t("backup.repaired_target_refs",{n:String(e.repaired_target_refs)})}
    `:d} + ${e.preserved_unresolved_refs?p` +
    ${this.host._t("backup.preserved_unresolved_refs",{n:String(e.preserved_unresolved_refs)})}
    ${this.host._t("backup.preserved_unresolved_hint")}
    + `:d} + ${h.length?p`
    + ${this.host._t("backup.import_details")} +
    + ${h.map(([t,e])=>p`${this.host._t(`backup.import_detail.${t}`,{n:String(e)})}`)} + ${(r.examples||[]).slice(0,8).map(t=>p` + ${t.owner} → ${t.reference} + `)} +
    +
    `:d} + ${"full"===e.kind?p` +
    ${this.host._t("backup.replace_warning")}
    + ${"foreign"===e.source?p`
    ${this.host._t("backup.foreign_bookkeeping")}
    `:d}`:p` +
    ${this.host._t("backup.final_name")}${e.space_title}
    +
    ${this.host._t("backup.target_settings")}
    + ${e.duplicates?p`
    ${this.host._t("backup.duplicates",{n:String(e.duplicates)})} + + +
    `:d}`} + ${e.content?.length?p`
    + ${this.host._t("backup.content")} + ${s.length?p`${this.host._t("backup.decor_images_summary",{assets:i,objects:s.length,missing:a})}`:d} + ${e.content.filter(t=>"decor_asset"!==t.kind).map(t=>p`${t.url} · ${this.host._t("available"===t.state?"backup.content_available":"external"===t.state?"backup.content_external":"backup.content_detach_required")}`)} +
    `:d} + ${e.confirmation_required?p``:d} + `:d} +
    +
    + + + ${e?p``:d} +
    +
    `}_setFillColor(t,e){const s=this.host._settingsDialog;this.host._settingsDialog={...s,colors:{...s.colors,[t]:{...s.colors[t],...e}}}}_updateDecorStyle(t){this.host._decorStyle=t,null!==this._decorStylePersistTimer&&window.clearTimeout(this._decorStylePersistTimer),this._decorStylePersistTimer=window.setTimeout(()=>{this._decorStylePersistTimer=null,this._persistDecorStyle()},1e3)}_persistDecorStyle(){const t=this.host._serverCfg;if(!t)return;const e=Xs(this.host._decorStyle),s={...t.settings},i=JSON.stringify(s.decor_default_style??null);e?s.decor_default_style=e:delete s.decor_default_style,JSON.stringify(s.decor_default_style??null)!==i&&(this.host._serverCfg={...t,settings:s},this._saveConfig())}async _saveSettingsDialog(){const t=this.host._settingsDialog;if(!t||t.busy)return;this.host._settingsDialog={...t,busy:!0};let e=null;try{const s=this.host._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Zs);let o={...s.settings};i?delete o.fill_colors:o.fill_colors=t.colors;const a=this.host._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(a)&&a>0&&300!==Math.round(a)?o.glow_radius_cm=Math.round(a):delete o.glow_radius_cm,t.bgColor?o.bg_color=t.bgColor:delete o.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?o.north_deg=t.northDeg:delete o.north_deg,o.bg_mode=t.bgMode,t.sunRays?o.sun_rays=!0:delete o.sun_rays,t.showRoomTooltip?delete o.show_room_tooltip:o.show_room_tooltip=!1,o=Qs(o,t.zigbeeTopology),delete o.weather_entity;const n={...s,settings:o};e=u(s,n,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=n,await this._saveConfigNow(e),!t.showRoomTooltip&&this.host._tip?.room&&(this.host._tip=null),this.host._settingsDialog=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.saved"))}catch(t){e&&m(this.host,e,_),this.host._settingsDialog&&(this.host._settingsDialog={...this.host._settingsDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_boolInput(t,e,s=!1){const i=t=>e(!!t.target.checked);return customElements.get("ha-switch")?p``:p``}_rangeInput(t,e,s,i,o,a=!1,n){const r=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?p``:p``}_renderColorRow(t,e){const s=this.host._settingsDialog.colors[t];return p`
    + {this._setFillColor(t,{c:e.detail.color,a:e.detail.opacity})}}> +
    `}_renderAlignDialog(){const t=this.host._alignDialog,e=t.report,s=t.changed&&!t.preflight?.ok,i=t.preflight?.failures||[],o=i.slice(0,3).map(t=>t.displayName),a=o.length?o.join(", "):this.host._t("gs.align_preflight_space",{n:"1"}),n=Math.max(0,i.length-o.length),r=n?this.host._t("gs.align_preflight_more",{n:String(n)}):"",l=e.spaceRefsRemapped+e.roomRefsRemapped+e.positionsRemapped+e.markersDetached,h=e.migrated+e.canonicalized+e.coordsCanonicalized+e.wallSegmentsMigrated+e.wallsMerged+e.spansMerged+e.partitionsMerged+e.partitionsReconciled+e.openingsRehosted+e.redundantDraftsRemoved,c=e.moved+e.rotated+e.removedDrafts+e.coordsCanonicalized+e.wallsStraightened,u=Math.ceil(10*e.maxStraightenShiftCm)/10,_=(this.host._serverCfg?.spaces||[]).find(t=>String(t?.id||"")===e.maxStraightenSpace),m=(this.host._serverCfg?.spaces||[]).length>1&&_?String(_.title||_.id):"",g=e.orphanRoomLabelsRemoved+e.orphanDevicePositionsRemoved+e.orphanGroupPositionsRemoved,f=e.liveMissingPositions.map(t=>t.name).filter(Boolean),b=f.slice(0,3).join(", "),v=Math.max(0,f.length-3),y=b?this.host._t("gs.optimize_live_names",{names:b,more:v?this.host._t("gs.optimize_reference_more",{n:String(v)}):""}):"",$=e.unverifiedPositions.some(t=>"registry_unavailable"===t.reason),w=t=>e.removedPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_removed"):e.liveMissingPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_live"):this.host._t("gs.optimize_detail_unverified"),k=t=>this.host._t("room_label"===t?"gs.optimize_detail_room_label":"group"===t?"gs.optimize_detail_group":"device"===t?"gs.optimize_detail_device":"gs.optimize_detail_unknown"),x=[...e.removedPositions,...e.liveMissingPositions.filter(t=>!e.removedPositions.some(e=>e.id===t.id)),...e.unverifiedPositions],S=x.slice(0,10),M=Math.max(0,x.length-S.length);return p`{this.host._alignDialog=null,this.host._preflightClipboardFallback=null}}> +
    + ${s?p` +

    ${this.host._t("gs.align_preflight_failed",{spaces:a,more:r})}

    + ${i.slice(0,10).map(t=>p`

    + ${t.displayName}: ${this.host._t(`gs.preflight_reason_${t.reason}`)} +

    `)} + ${i.length>10?p`

    + ${this.host._t("gs.align_preflight_more",{n:String(i.length-10)})} +

    `:d} +
    ${this.host._t("gs.align_preflight_hint")}
    + ${this._preflightVersionsDiffer()?p` +
    ${this.host._t("gs.preflight_update_hint")}
    `:d} +
    + +
    + ${this.host._preflightClipboardFallback?p`
    + ${this.host._t("gs.preflight_copy")} +
    ${this.host._preflightClipboardFallback}
    +
    `:d}`:t.changed?p` + ${e.moved?p`

    ${this.host._t("gs.align_count",{n:String(e.moved),total:String(e.total),cm:String(t.cm)})}

    `:d} + ${e.latticeCoordinatesCanonicalized?p` +

    ${this.host._t("gs.optimize_lattice_summary",{n:String(e.latticeCoordinatesCanonicalized),cm:ti(e.latticeMaxShiftCm)})}

    + ${e.latticeSpaces.map(t=>p`

    ${this.host._t("gs.optimize_lattice_space",{space:t.space,n:String(t.canonicalized),far:String(t.far)})}

    `)} + `:d} + ${t.where?p`

    ${this.host._t("gs.align_where",{s:t.where})}

    `:d} + ${e.rotated?p`

    ${this.host._t("gs.align_turned",{n:String(e.rotated)})}

    `:d} + ${e.removedDrafts?p`

    ${this.host._t("gs.align_removed_drafts",{n:String(e.removedDrafts)})}

    `:d} + ${e.redundantDraftsRemoved?p`

    ${this.host._t("gs.optimize_redundant_drafts",{n:String(e.redundantDraftsRemoved)})}

    `:d} + ${e.wallSegmentsMigrated?p`

    ${this.host._t("gs.wall_segments_migrated",{n:String(e.wallSegmentsMigrated)})}

    `:d} + ${e.legacyZeroWallsMigrated?p`

    ${this.host._t("gs.zero_walls_migrated",{n:String(e.legacyZeroWallsMigrated)})}

    `:d} + ${h?p`

    ${this.host._t("gs.optimize_changes",{m:String(e.migrated),c:String(e.canonicalized),p:String(e.coordsCanonicalized),w:String(e.wallsMerged),s:String(e.spansMerged),i:String(e.partitionsMerged)})}

    `:d} + ${e.partitionsReconciled?p`

    ${this.host._t("gs.optimize_coincident_partitions",{n:String(e.partitionsReconciled)})}

    `:d} + ${e.openingsRehosted?p`

    ${this.host._t("gs.optimize_openings_rehosted",{n:String(e.openingsRehosted)})}

    `:d} + ${e.wallsStraightened?p`

    ${this.host._t("gs.optimize_walls_straightened",{n:String(e.wallsStraightened),cm:String(u)})}

    `:d} + ${m?p`

    ${this.host._t("gs.optimize_walls_straightened_where",{s:m})}

    `:d} + ${e.glowSpacesMigrated||e.glowRoomsMigrated?p`

    ${this.host._t("gs.optimize_glow_migration",{spaces:String(e.glowSpacesMigrated),rooms:String(e.glowRoomsMigrated)})}

    `:d} + ${c?p`
    ${this.host._t("gs.align_warn")}
    `:d}`:p`

    ${this.host._t(e.liveMissingPositions.length||e.unverifiedPositions.length||e.nestedRefsUnresolved?"gs.optimize_no_automatic_changes":"gs.align_none")}

    `} + ${!s&&e.wallsStraightenSkipped?p`

    ${this.host._t("gs.optimize_walls_straighten_skipped",{n:String(e.wallsStraightenSkipped)})}

    `:d} + ${l?p`

    ${this.host._t("gs.optimize_references",{spaces:String(e.spaceRefsRemapped),rooms:String(e.roomRefsRemapped),positions:String(e.positionsRemapped),detached:String(e.markersDetached)})}

    `:d} + ${g?p`

    ${this.host._t("gs.optimize_orphans_removed",{total:String(g),rooms:String(e.orphanRoomLabelsRemoved),devices:String(e.orphanDevicePositionsRemoved),groups:String(e.orphanGroupPositionsRemoved)})}

    `:d} + ${e.liveMissingPositions.length?p`
    +

    ${this.host._t(t.removeLiveMissingPositions?"gs.optimize_live_positions_remove":"gs.optimize_live_positions",{n:String(e.liveMissingPositions.length),names:y})}

    + + ${t.removeLiveMissingPositions?p`
    + ${this.host._t("gs.optimize_live_selected")} +
    `:d} +
    `:d} + ${e.unverifiedPositions.length?p``:d} + ${e.nestedRefsUnresolved?p``:d} + ${x.length?p`
    + ${this.host._t("gs.optimize_details")} +
      + ${S.map(t=>p`
    • ${this.host._t("gs.optimize_detail_item",{status:w(t),kind:k(t.kind),id:t.id,space:t.spaceId})}
    • `)} +
    + ${M?p`
    ${this.host._t("gs.optimize_details_more",{n:String(M)})}
    `:d} +
    `:d} +
    +
    + + + ${t.changed&&t.preflight?.ok?p` + `:d} +
    +
    `}_renderSettingsDialog(){return p`this.host._settingsDialog=null}> +
    +
    ${ko(l(this.host.hass,this.host._config?.language),"gs.hint")}
    + this.host._settingsDialog={...this.host._settingsDialog,zigbeeTopology:t.detail}}> + + ${this._renderColorRow("light_on","gs.light_on")} + ${this._renderColorRow("light_off","gs.light_off")} + ${this._renderColorRow("light_none","gs.light_none")} + + ${this._renderColorRow("temp_cold","gs.temp_cold")} + ${this._renderColorRow("temp_ok","gs.temp_ok")} + ${this._renderColorRow("temp_hot","gs.temp_hot")} + + ${this._renderColorRow("lqi_low","gs.lqi_low")} + ${this._renderColorRow("lqi_high","gs.lqi_high")} + + ${this._renderColorRow("glow_base","gs.glow_base")} + ${this._renderColorRow("glow_light","gs.glow_light")} +
    + + ${this._help("gs.glow_radius.help")} + {const e=Oh(t.target.value);null!=e&&e>0&&(this.host._settingsDialog={...this.host._settingsDialog,glowRadius:e})}} /> + ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} +
    + + ${this._renderColorRow("wall_fill","gs.wall_fill")} + +
    + + ${this._help("gs.bg_mode.help")} + +
    + ${"static"===this.host._settingsDialog.bgMode?p`
    + {this.host._settingsDialog={...this.host._settingsDialog,bgColor:t.detail.color}}}> + ${this.host._settingsDialog.bgColor?p``:p`${this.host._t("gs.bg_theme")}`} +
    `:d} + + ${ei(this.host.hass)?d:p`
    ${this.host._t("gs.sun_missing")}
    `} +
    + ${this.host._renderCompass()} +
    +
    + + ${this._help("gs.north.help")} +
    +
    + {const e=t.target.value.trim(),s=""===e?null:Math.round(Number(e));this.host._settingsDialog={...this.host._settingsDialog,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} /> + ${null!==this.host._settingsDialog.northDeg?p``:d} +
    +
    +
    + + ${this.host._canEdit?p` + +
    ${this.host._t("gs.backup_hint")}
    +
    + + + + this._pickBackupImport(t)} /> + + ${this.host._canOptimizeUndo&&"import"===this.host._undoKind?p` + `:d} +
    `:d} + +
    ${this.host._t("gs.grid_hint")}
    +
    + +
    + ${this.host._canOptimizeUndo&&"import"!==this.host._undoKind?p`
    + +
    `:d} +
    +
    + + + + +
    +
    `}_rulesSet(t){this.host._rulesDialog={...this.host._rulesDialog,rules:t}}async _saveRules(){const t=this.host._rulesDialog;if(!t||t.busy)return;const e=t.rules.filter(t=>t.pattern.trim()&&t.icon.trim());this.host._rulesDialog={...t,busy:!0};try{const t=this.host._serverCfg,s=JSON.stringify(e)===JSON.stringify(le),i={...t.settings};s?delete i.icon_rules:i.icon_rules=e,this.host._serverCfg={...t,settings:i},await this._saveConfigNow(),this.host._rulesDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("rules.saved"))}catch(t){this.host._rulesDialog&&(this.host._rulesDialog={...this.host._rulesDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_renderRulesDialog(){const t=this.host._rulesDialog,e=si(t.rules),s=t.test.trim()?ii(t.test,"",e):null,i=(e,s)=>{const i=[...t.rules],o=e+s;o<0||o>=i.length||([i[e],i[o]]=[i[o],i[e]],this._rulesSet(i))};return p`this.host._rulesDialog=null}> +
    +
    ${this.host._t("rules.hint")}
    +
    + this.host._rulesDialog={...t,test:e.target.value}} /> + ${s?p`${s}`:d} +
    + ${t.rules.map((e,s)=>{const o=""!==e.pattern.trim()&&!oi(e.pattern);return p`
    + {const o=[...t.rules];o[s]={...e,pattern:i.target.value},this._rulesSet(o)}} /> + {const o=[...t.rules];o[s]={...e,icon:i.target.value},this._rulesSet(o)}} /> + + i(s,-1)}> + i(s,1)}> + this._rulesSet(t.rules.filter((t,e)=>e!==s))}> +
    `})} + +
    +
    + + + + +
    +
    `}_saveKioskScale(t){this.host._kioskScale={...this.host._kioskScale,...t};try{localStorage.setItem("houseplan_card_kiosk_v1",JSON.stringify(this.host._kioskScale))}catch{}this.host.requestUpdate()}_renderKioskDialog(){const t=this.host._kioskScale,e=(e,s)=>p` +
    + ${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))} + ${Math.round(100*t[e])}% +
    `;return p`this.host._kioskDialog=!1}> +
    +
    ${this.host._t("kiosk.hint")}
    + ${e("icon",this.host._t("kiosk.icon_scale"))} + ${e("font",this.host._t("kiosk.font_scale"))} +
    +
    + + + +
    +
    `}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return d;const s=e.marker?.vacuum||{},o=this.host._vacAllCamerasFor===e.id,a=this.host._vacSourceResolution(e,o),n=a.entityId,r=a.candidates.find(t=>t.entityId===n)||null,l=n?ai(this.host.hass?.states[n]?.attributes):null,h=!1!==s.live&&("ok"===a.status||"unsupported"===a.status),c=this._vacPlanRoomAnchors(e.space),u=l?ni(l.rooms,c.map(t=>t.name)):0,_=!!(h&&l&&u>=3),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=a.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=o?a.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>p` + `,$=a.candidates.some(t=>g.has(t.entityId)&&"known_xcme_incomplete"===t.category)||!!a.pinned&&!!n?.startsWith("camera.")&&!r?.hasPosition;return p` + +
    +
    +
    ${this.host._t("vac.diag_source")}${n||this.host._t("vac.source_none")}
    + ${r?.platform?p`
    ${this.host._t("vac.diag_platform")}${r.platform}
    `:d} +
    ${this.host._t("vac.diag_status")}${this.host._t(`vac.source_status_${a.status}`)}
    +
    ${this.host._t("vac.diag_position")}${l?.pos?this.host._t("common.yes"):this.host._t("common.no")}
    +
    ${this.host._t("vac.diag_rooms")}${i(this.host._t("vac.diag_rooms_value"),{total:String(l?.rooms.length||0),matched:String(u),readiness:this.host._t(u>=3?"vac.autocal_ready":"vac.autocal_not_ready")})}
    +
    ${this.host._t("vac.diag_path")}${l?.path.length?this.host._t("common.yes"):this.host._t("common.no")}
    +
    ${this.host._t("vac.diag_map")}${l?.mapId??"default"}
    +
    + ${"missing"===a.status||"disabled"===a.status||"unverified"===a.status?p`
    + ${this.host._t(`vac.source_banner_${a.status}`)} + +
    `:d} + ${$?p`
    + ${this.host._t("vac.xcme_hint")} +
    attributes:
    +  - vacuum_position
    +  - rooms
    +  - path
    +  - map_name
    +
    `:d} +
    + ${this.host._t("vac.choose_source")} +
    + + ${f.map(y)} +
    {t.currentTarget.open?this.host._vacOpenAllCameras(e):(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null)}}> + ${this.host._t("vac.all_cameras")} +
    ${this.host._t("vac.all_cameras_warn")}
    + ${o?b.length?b.map(y):p`
    ${this.host._t("vac.all_cameras_empty")}
    `:d} +
    +
    +
    +
    + ${_?p``:d} + ${h?p``:d} + ${this.host._t("vac.documentation")} +
    + ${l?p` + + + + ${Io(this,e,a)} + `:d} +
    `}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i,o=""){return hh(this,t,e,s,i,o)}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:at(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=li(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}async _vacAutoCalibrate(t){if(this.host._markerDialog?.busy)return;const e=this.host._vacSource(t),s=e?ai(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacMapId(t,s),a=function(t,e,s,i,a){const n=o(t,e,s,i).find(t=>t.source===i&&t.map_id===a);return{space:n?.space||s,routeId:n?.id||""}}(t.id,t.marker?.vacuum??null,t.space,e,i),n=hi(s.rooms,this._vacPlanRoomAnchors(a.space));if(!n)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const r=this.host._serverCfg?.spaces?.find(t=>t.id===a.space),l=Number(r?.cell_cm),h=Number.isFinite(l)&&l>0?l:5,c=ci(n.residual,this.host._gridPitch,h);c>di?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length,error:Te(c,"mi"===this.host.hass?.config?.unit_system?.length)}:await async function(t,e){if(rh.has(t))return;rh.add(t);const s=t.host,i=s._markerDialog,o=i?{...i,busy:!0}:null;o&&(s._markerDialog=o,s.requestUpdate());try{await hh(t,e.markerId,e.source,e.mapId,e.matrix,e.routeId)&&s._showToast(s._t("vac.autocal_done",{rooms:String(e.rooms)}))}finally{rh.delete(t),o&&s._markerDialog===o&&(s._markerDialog={...o,busy:!1},s.requestUpdate())}}(this,{markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length})}_vacApplyCalibrationProposal(t){return async function(t,e){const s=t.host,i=s._vacCalConfirm;if(!i||i.busy)return;if(e){const t=s._devices.find(t=>t.id===i.markerId),e=n(i.matrix);if(!t||!e)return;const o=i.space||t.space;if(o!==s._space&&!s._commitSpace(o))return;return s._vacCalConfirm=null,s._markerDialog=null,void(s._vacFit={markerId:i.markerId,source:i.source,routeId:i.routeId,mapId:i.mapId,p:e,drag:null})}const o={...i,busy:!0};s._vacCalConfirm=o,s.requestUpdate();const a=await hh(t,i.markerId,i.source,i.mapId,i.matrix,i.routeId);s._vacCalConfirm===o&&(s._vacCalConfirm=a?null:{...i,busy:!1},s.requestUpdate()),a&&s._showToast(s._t("vac.autocal_done",{rooms:String(i.rooms)}))}(this,t)}_vacStartFit(t,e=""){if(this.host._markerDialog?.busy)return;const s=this.host._vacSource(t),i=s?ai(this.host.hass?.states[s]?.attributes):null;if(!s||!i)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const l=this._vacMapId(t,i),h=function(t,e,s){const i=o(t,e,s.dockSpace,s.source),l=i.find(t=>t.id===s.routeId)||i.find(t=>t.source===s.source&&t.map_id===s.mapId)||null,h=l?.space||s.dockSpace,c=s.viewBoxOf(h);if(!c)return null;const d=a(l?l.calibration:e?.calibration?.[s.mapId]),p=d&&n(d)||r(s.rooms,c);return{space:h,routeId:l?.id||"",params:p}}(t.id,t.marker?.vacuum??null,{routeId:e,source:s,mapId:l,dockSpace:t.space,rooms:i.rooms,viewBoxOf:t=>this.host._spaceModelById(t)?.vb??null});h&&(this.host._markerDialog=null,(h.space===this.host._space||this.host._commitSpace(h.space))&&(this.host._vacFit={markerId:t.id,source:s,mapId:l,routeId:h.routeId,p:h.params,drag:null}))}_vacFitSave(){return async function(t,e){const s=t.host,i=s._vacFit;if(!i||i.busy)return;const o={...i,busy:!0,drag:null};s._vacFit=o,s.requestUpdate();const a=await hh(t,i.markerId,i.source,i.mapId,e(i.p),i.routeId);s._vacFit===o&&(s._vacFit=a?null:{...i,busy:!1,drag:null},s.requestUpdate()),a&&s._showToast(s._t("vac.cal_done"))}(this,ui)}_vacFitTurn(t){const e=this.host._vacFit;if(!e||e.busy)return;const s=ai(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:pi(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),s.busy)return;if("pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=ai(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=ui(i.p0),[r,l]=_i(n,e[0],e[1]),[h,c]=_i(n,i.fx,i.fy),d=Math.hypot(r-h,l-c)||1,[p,u]=[2*r-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*a-c)/2,m=Math.max(.05,_/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:pi(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return d;const s=ai(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return d;const i=ui(e.p),o=[],a=[],n=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>_i(i,t,e));e.forEach(([t,e])=>{a.push(t),n.push(e)});const[s,r]=_i(i,t.cx,t.cy);o.push(f` + ${t.name}`)}let r=d;if(s.pos){const[e,o]=_i(i,s.pos.x,s.pos.y);r=f``}const l=[];if(a.length){const e=(()=>{const t=i[0]*i[4]-i[1]*i[3];return(e,s)=>[(i[4]*(e-i[2])-i[1]*(s-i[5]))/t,(-i[3]*(e-i[2])+i[0]*(s-i[5]))/t]})(),s=Math.min(...a),o=Math.max(...a),r=Math.min(...n),h=Math.max(...n),c=.022*t.w,d=c/4;for(const[t,i,a,n]of[[s,r,o,h],[o,r,s,h],[o,h,s,r],[s,h,o,r]]){const s=e(a,n);l.push(f` + `)}}return p`this._vacFitPointer(e,t)} + @pointermove=${e=>this._vacFitPointer(e,t)} + @pointerup=${e=>this._vacFitPointer(e,t)} + @pointercancel=${e=>this._vacFitPointer(e,t)}>${o}${r}${l}`}_resetRoomDialogFields(){this.host._roomEditId=null,this.host._roomFill="",this.host._roomCustomFill=null,this.host._roomTempSrc="",this.host._roomHumSrc="",this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomNameScale=1,this.host._roomLabelScale=1}_openRoomEdit(t){if(!t.id)return;this.host._roomEditId=t.id,this.host._nameSel=t.name||"",this.host._areaSel=t.area||"",this.host._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this.host._roomCustomFill=e&&"object"==typeof e?Os(e,Es(this.host._curSpaceCfg).customFill):null,this.host._roomTempSrc=t.settings?.temp_source||"",this.host._roomHumSrc=t.settings?.hum_source||"",this.host._roomNameScale=mi(t.settings?.name_scale),this.host._roomLabelScale=mi(t.settings?.label_scale),this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this.host._roomFill&&(t.fill_mode=this.host._roomFill),this.host._roomCustomFill&&(t.custom_fill=this.host._roomCustomFill),this.host._roomTempSrc&&(t.temp_source=this.host._roomTempSrc),this.host._roomHumSrc&&(t.hum_source=this.host._roomHumSrc),1!==this.host._roomNameScale&&(t.name_scale=this.host._roomNameScale),1!==this.host._roomLabelScale&&(t.label_scale=this.host._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this.host._curSpaceCfg,e=t?.rooms.find(t=>t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=Z(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(Q(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Wh,y:s.y*Wh};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},Gh(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),l=this.host._viewOr(n),h=(t.clientX-this.host._drag.sx)/r.width*l.w,c=(t.clientY-this.host._drag.sy)/r.height*l.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=pe(this.host._drag.ox+h),p=pe(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},Gh(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/Wh,y:a.y/Wh,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return d;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=He(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return d;const o=(i[0]-s.x)/s.w*100,a=(i[1]-s.y)/s.h*100;return p``}_alignCandidates(){const t=[],e=this.host._spaceModel();if(this.host._markup){if(!e)return t;if(this.host._drag?.id.startsWith("rl_")){const s=this.host._drag.id.slice(3);for(const i of e.rooms){if(!i.name||i.id===s)continue;const e=this._labelPos(i,this.host._space);t.push([e.x,e.y])}return t}for(const s of e.rooms){const e=at(s);if(e)for(const s of e)t.push(s)}if("draw"===this.host._tool)for(const e of this.host._path)t.push(e);if("split"===this.host._tool&&this.host._splitSel?.pts)for(const e of this.host._splitSel.pts)t.push(e);return t}if("devices"===this.host._mode){const e=this.host._deviceDrag?.id??this.host._drag?.id;for(const s of this.host._devices){if(s.space!==this.host._space||s.id===e||"ha_disabled"===s.bindingStatus?.kind)continue;const i=this.host._pos(s);t.push([i.x,i.y])}return t}if("decor"===this.host._mode){const e=this.host._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this.host._decorDraft&&t.push(this.host._decorDraft.a),t}return t}_renderAlignGuides(){const t=this.host._alignPoint;if(!t)return f``;const e=this.host._drag?.id.startsWith("rl_")?.5*this.host._gridPitch:.05*this.host._gridPitch,s=gi(t,this._alignCandidates(),e);if(!s.length)return f``;const i=this.host._gridPitch,o=1.5*i;return f` + ${s.map(e=>{const[s,a,n,r]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return f` + `})} + `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180,s=k(15,this.host._cellCm);return f``}_renderOpeningDimensionGuides(t){const e=t.labels.flatMap(t=>t.dimension?[t.dimension]:[]);if(!e.length)return f``;const s=this._cssPxToRender(4);return f``}_renderOpeningPlacementPreview(){const t=this.host._openingPreview;if(!t)return f``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:fi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:t.face},s="passage"===t.type?bi(t,k(this.host._gridPitch,this.host._cellCm)):null;return f` + `}_renderOpeningDialog(){const t=this.host._openingDialog,e=this.host._spaceModel()?.partitions||[],s=t.id?this.host._curSpaceCfg?.openings?.find(e=>e.id===t.id):null,i={id:t.id||"preview",type:t.type,x:t.x/Wh,y:t.y/this.host._spaceH,angle:t.angle,length:s&&!t.lengthTouched?s.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/Wh,...t.host?{host:t.host}:{}},o=fs(s,i),a=t.host?o?_t(i,e,Wh,this.host._cellCm,this.host._gridPitch):nt(i,e,Wh,this.host._cellCm,this.host._gridPitch):null,n="does-not-fit-jamb"===a?.reason,r=t.host?e.find(e=>e.id===t.host.id):null,l=r?Te(r.cm/2,this.host._imperial):"",h=!!t.host&&!a?.resolved&&!n,c="gate"===t.type?"mdi:gate":"window"===t.type?"mdi:window-closed-variant":"passage"===t.type?"mdi:arch":"mdi:door",u=(e,s)=>{const i="contact"===e?t.contact:t.lock,o="contact"===e?!!t.contactOpen:!!t.lockOpen,a="contact"===e?t.contactFilter||"":t.lockFilter||"",n=s.find(t=>t.value===i),r=n?.label||this.host.hass.states[i]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[i]?.name||i,l=Qi(s,a);return p` + + ${o?p`
    + this._filterOpeningEntities(e,t.target.value)} /> +
    + + ${l.map(t=>p` + `)} + ${l.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} +
    +
    `:d}`};return p`this.host._openingDialog=null}> +
    + ${t.host?p` +
    + ${h||n?p``:d} + ${h?this.host._t("opening.partition_orphan"):n?this.host._t("opening.partition_jamb_margin",{distance:l}):this.host._t("opening.host_partition")} +
    `:d} + + + + + + + + {const s=Oh(e.target.value);null!=s&&(this.host._openingDialog={...t,lengthCm:s,lengthTouched:!0})}} /> + + ${"passage"===t.type&&(t.contact||t.lock)?p`
    + + ${this.host._t("opening.passage_binding_warning")} +
    `:d} + + ${"passage"!==t.type?p` + ${u("contact",this._contactCandidates())} + ${t.contact?p``:d}`:d} + + ${"door"===t.type||"gate"===t.type?p` + ${u("lock",this._lockCandidates())}`:d} + + ${"gate"!==t.type&&"passage"!==t.type?p``:d} + ${"passage"!==t.type?p``:d} +
    + +
    `}_gridLevels(){const t=this.host._stageEl,e=this.host._viewOr(this.host._baseVb()),s=t&&t.clientWidth&&e.w?t.clientWidth/e.w:1;return $i(this.host._gridPitch,s)}_renderMarkupDefs(t){const e=this._gridLevels();if(!e)return f``;const s=this.host._gridPitch*e.fine,i=this.host._gridPitch*e.coarse,o=this.host._gridPitch*e.fine*.14;return f` + + + + + + + + + + + + + + `}_renderPhysicalEditorLayer(){const t=this.host._spaceModel();if(!t)return f``;const e=this.host._gridPitch,s=this.host._viewOr(this.host._baseVb()),i=this.host._stageEl,o=i?.clientWidth?s.w/i.clientWidth:e/8,a=Math.max(.22*e,8*o),n=Math.max(a,12*o),r=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`,l=(t,e)=>this.host._physicalSel?.kind===t&&this.host._physicalSel.id===e||"column"===t&&this.host._duplicateColumnId===e,h=(t.room_drafts||[]).flatMap(t=>t.points.slice(0,-1).map((e,s)=>{const i=t.points[s+1],a=Number(t.segments[s]?.cm),n=x(Number.isFinite(a)?a:15,this.host._cellCm,this.host._gridPitch);return f`{this.host._physicalSel={kind:"draft",id:t.id,segment:s},this._registerPhysicalTap("draft",t.id,s)}}>`})),c=(t.partitions||[]).map(t=>{const e=De(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);return e?f`this._physicalDown(e,"partition",t.id)} + @pointermove=${t=>this._physicalMove(t)} + @pointerup=${t=>this._physicalUp(t)}>`:f`this._physicalDown(e,"partition",t.id)} + @pointermove=${t=>this._physicalMove(t)} + @pointerup=${t=>this._physicalUp(t)}>`}),p=(t.wall_columns||[]).map(t=>{const e=this.host._physicalRotate?.id===t.id?{...t,angle:this.host._physicalRotate.angle}:t,s=Me(e,this.host._cellCm,this.host._gridPitch);return f`this._physicalDown(e,"column",t.id)} + @pointermove=${t=>this._physicalMove(t)} + @pointerup=${t=>this._physicalUp(t)}>`}),u=this.host._physicalDrag,_=(()=>{if(!u?.moved)return d;if("partition"===u.kind&&0===Number(u.base.cm)){const t=u.base;return f``}const t="partition"===u.kind?De(u.base.a,u.base.b,u.base.cm,this.host._cellCm,this.host._gridPitch):Me(u.base,this.host._cellCm,this.host._gridPitch);return t?f``:d})(),m=(()=>{const s=this.host._physicalSel;if(!s)return d;if("draft"===s.kind){const e=t.room_drafts.find(t=>t.id===s.id);return e?f` + t.join(",")).join(" ")}> + ${e.points.map(t=>f``)} + `:d}if("partition"===s.kind){const e=t.partitions.find(t=>t.id===s.id);if(!e)return d;const i=De(e.a,e.b,e.cm,this.host._cellCm,this.host._gridPitch),o=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2;return f` + ${i?f``:f``} + + + + `}const i=t.wall_columns.find(t=>t.id===s.id);if(!i)return d;const l=this.host._physicalRotate?.id===i.id?{...i,angle:this.host._physicalRotate.angle}:i,h=Me(l,this.host._cellCm,this.host._gridPitch);if("square"!==l.shape)return f` + + + `;const c=x(l.cm,this.host._cellCm,this.host._gridPitch),p=ge(l.angle)*Math.PI/180,u=Math.sin(p),_=-Math.cos(p),m=[l.center[0]+u*c/2,l.center[1]+_*c/2],g=[m[0]+u*Math.max(e,24*o),m[1]+_*Math.max(e,24*o)];return f` + + + + this._physicalRotateDown(t,i)} + @pointermove=${t=>this._physicalRotateMove(t)} + @pointerup=${t=>this._physicalRotateUp(t)}> + `})();return f`${h}${c}${p}${_}${m}`}_renderHiddenWallDiagnosticOverlay(){if(!this.host._markup)return f``;const t=this._hiddenWallDiagnosticSnapshot().value;if(!t.segments.length)return f``;const e=x(5,this.host._cellCm,this.host._gridPitch);return f``}_renderPlanSnapOverlay(){if(!this.host._markup)return f``;const t=this._planSnapGeometrySnapshot().value,e=this.host._activePlanSnapCandidate,s=new Set(this.host._activePlanSnapConflicts.map(t=>t.key)),i=x(5,this.host._cellCm,this.host._gridPitch),o=x(10,this.host._cellCm,this.host._gridPitch);return f``}_syncPlanSnapActiveMarker(t){const e=this.host.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(e){if(e.setAttribute("class",`plan-snap-node${t?" active":""}${"line"===t?.kind?" dynamic":""}`),e.setAttribute("data-active",t?"true":"false"),e.setAttribute("visibility",t?"visible":"hidden"),!t)return e.removeAttribute("data-kind"),void e.removeAttribute("data-key");e.setAttribute("data-kind",t.kind),e.setAttribute("data-key",t.key),e.setAttribute("cx",String(t.point[0])),e.setAttribute("cy",String(t.point[1])),e.setAttribute("r",String(x(10,this.host._cellCm,this.host._gridPitch)))}}_syncPlanSnapConflictMarkers(t){const e=new Set(t.map(t=>t.key));for(const t of this.host.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])t.setAttribute("class","plan-snap-node"+(e.has(t.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(t){let e=0;const s=this.host._spaceModel();if(!s)return null;if("partition"===t.sourceKind)e=Number(s.partitions.find(e=>e.id===t.sourceId)?.cm)||0;else if("draft"===t.sourceKind){const i=/^(.*):(\d+)$/.exec(t.sourceId),o=i?s.room_drafts.find(t=>t.id===i[1]):null;e=o&&i&&Number(o.segments[Number(i[2])]?.cm)||0}else e=wi(s.rooms,this.host._spaceWalls,this.host._openCuts(),[t.a[0],t.a[1],t.b[0],t.b[1]],this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,Wh);return e>0?{a:[...t.a],b:[...t.b],halfDepth:x(e,this.host._cellCm,this.host._gridPitch)/2}:null}_drawPreviewJoinPatchD(t,e){if(t.length<2)return"";const s=[];for(let i=0;i+10&&s.push({a:t[i],b:t[i+1],halfDepth:e[i]});if(!s.length)return"";const i=2e-4*this.host._gridPitch,o=this._planSnapGeometrySnapshot().value.segments.filter(e=>t.some(t=>st(t,[e.a[0],e.a[1],e.b[0],e.b[1]])<=i)).map(t=>this._planSnapPhysicalSegment(t)).filter(t=>!!t);return ki([...s,...o],i).map(t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(t){const e=this.host._openCuts(),s=this.host._thickWallCuts(),i=e.concat(s),o=i.length?et(this.host._segments,i,.02*this.host._gridPitch):this.host._segments,a=this.host._path,n=this.host._gridPitch,r=this.host._viewOr(this.host._baseVb()),l="draw"===this.host._tool?this.host._drawWallCm:null,h=(()=>"draw"===this.host._tool&&a.length&&null!=l?this.host._contourClosed?a:this.host._cursorPt?[...a,this.host._cursorPt]:a.length>=2?a:null:null)(),c=h?or(h.length-1,this.host._contourClosed?[...this.host._draftSegmentCms,this.host._closingWallCm??void 0]:this.host._draftSegmentCms,l,ht).map(t=>x(t,this.host._cellCm,this.host._gridPitch)/2):[],p=h?xi(h,x(l,this.host._cellCm,this.host._gridPitch)/2,this.host._contourClosed,c):"",u=h?this._drawPreviewJoinPatchD(h,c):"",_=this.host._wallFaceBatch?.candidates[this.host._wallFaceBatch.index]?.repair||this.host._wallRepairDiagnostic;return f` + ${this._gridLevels()?f``:d} + ${o.map(t=>f``)} + ${this._renderPhysicalEditorLayer()} + ${"column"===this.host._tool&&this.host._cursorPt&&this.host._drawWallCm?f`{const t={shape:"square",center:this.host._cursorPt,cm:this.host._drawWallCm};return`M ${Me(t,this.host._cellCm,this.host._gridPitch).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`})()}>`:d} + ${p?f` + `:d} + ${h&&0===l?f`t.join(",")).join(" ")}>`:d} + ${u?f` + `:d} + ${_?f``:d} + ${"split"===this.host._tool&&this.host._splitSel?.pts?.length?f`${this.host._splitSel.pts.length>1?f``:d} + ${this.host._splitSel.pts.map((t,e)=>f``)} + ${this.host._cursorPt?f``:d}`:d} + `}_renderActiveChainInk(){const t=this.host._path,e=this.host._gridPitch;return f` + ${t.length>1?f``:d} + ${t.length&&this.host._cursorPt&&"draw"===this.host._tool&&!this.host._contourClosed?f` + ${this.host._activePlanSnapCandidate||this.host._activePlanSnapConflicts.length?d:f``}`:d} + ${t.map((t,s)=>f``)} + `}_renderPartitionDeleteDialog(){const t=this.host._partitionDeleteDialog,e="mi"===this.host.hass?.config?.unit_system?.length;return p`this.host._partitionDeleteDialog=null}> +
    +

    ${this.host._t("confirm.delete_partition_openings_body",{count:t.openings.length})}

    +
      + ${t.openings.map(t=>p`
    • ${this.host._t("confirm.delete_partition_openings_item",{type:this.host._t(`opening.${t.type}`),length:Te(t.length*Wh/this.host._gridPitch*this.host._cellCm,e)})}
    • `)} +
    +
    +
    + + + +
    +
    `}_renderRoomDeleteDialog(){const t=this.host._roomDeleteDialog;return p`this.host._roomDeleteDialog=null}> +
    +

    ${this.host._t("confirm.delete_room_body")}

    +
    +
    + + + + +
    +
    `}_renderPhysicalDialog(){const t=this.host._physicalDialog,e="column"===t.kind;return p`this.host._physicalDialog=null}> +
    + ${e?p` + `:d} + +
    this.host._physicalDialog={...t,cm:e.target.value}} /> + ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    + ${e&&"square"===t.shape?p` + + this.host._physicalDialog={...t,angle:e.target.value}} />`:d} + ${t.length?p`
    ${this.host._t("physical.length")}: ${t.length}
    `:d} +
    + +
    `}_renderMarkupBar(){const t=this.host._geometryHistory.undoName,e=this.host._geometryHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    +
    + + + + + + + + + + ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} + + + + +
    +
    + +
    +
    `}_renderDevicesBar(){const t=this.host._devicePositionHistory.undoName,e=this.host._devicePositionHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    +
    + + + + + ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} +
    +
    + + + +
    +
    `}_discoveryFilterState(t){const e=this.host._settings,s=void 0!==t.draftGroupLights?t.draftGroupLights:!1!==e.group_lights,i=e.exclude_integrations,o=void 0!==t.draftExcluded?null===t.draftExcluded?[...Si]:t.draftExcluded:i?[...i]:[...Si],a=void 0!==t.draftExcluded?null===t.draftExcluded:!i,n=void 0!==t.draftGroupLights||void 0!==t.draftExcluded;return{groupLights:s,excluded:o.sort(),usesProductList:a,dirty:n}}_registryIntegrations(){const t=new Set,e=this.host._fullRegistryHass;for(const s of Object.values(e?.entities||{}))s?.platform&&t.add(String(s.platform));for(const s of Object.values(e?.devices||{})){const e=s?.identifiers?.[0],i=Array.isArray(e)?e[0]:null;i&&t.add(String(i))}return[...t].sort()}_discoveryFilterPreview(t){const e=this._discoveryFilterState(t),s=[e.groupLights?1:0,e.usesProductList?"p":e.excluded.join(","),this.host._cfgEpoch,this.host._regSignature].join("|");if(this._discoveryPreviewMemo?.key===s)return this._discoveryPreviewMemo.value;const i={hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),markers:this.host._markers,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||"",loc:t=>this.host._t(t),iconRules:this.host._iconRules},o=(t,e)=>new Set(to({...i,settings:t,excluded:e})),a=(t,e)=>new Set(eo({...i,settings:t,excluded:e}).map(t=>t.bindingRef||t.id)),n=Mi(this.host._settings),r={...this.host._settings,group_lights:e.groupLights},l=new Set(e.excluded),h=o(this.host._settings,n),c=o(r,l),d=a(this.host._settings,n),p=a(r,l);let u=0,_=0,m=0;const g=t=>t.includes("light.");for(const t of h)c.has(t)||(u++,g(t)&&m++);for(const t of c)h.has(t)||(_++,g(t)&&m++);for(const t of p)d.has(t)||(u++,g(t)&&m++);for(const t of d)p.has(t)||(_++,g(t)&&m++);const f={appear:u,hide:_,lights:m};return this._discoveryPreviewMemo={key:s,value:f},f}async _saveDiscoveryFilters(t){const e=this.host._serverCfg;if(!e)return;const s=this._discoveryFilterState(t),i={...e.settings};s.groupLights?delete i.group_lights:i.group_lights=!1,s.usesProductList?delete i.exclude_integrations:i.exclude_integrations=s.excluded,this.host._serverCfg={...e,settings:i},this.host._deviceInboxMemo=null,this._discoveryPreviewMemo=null,this.host._regSignature="",this.host._deviceInbox&&(this.host._deviceInbox={...t,draftGroupLights:void 0,draftExcluded:void 0}),this._saveConfig(),this.host._maybeRebuildDevices(),this.host.requestUpdate()}_renderDiscoveryFilters(t){const e=this._discoveryFilterState(t),s=e=>this.host._deviceInbox={...t,...e},i=t.filtersOpen&&e.dirty?this._discoveryFilterPreview(t):null,o=this._registryIntegrations().filter(t=>!e.excluded.includes(t));return p`
    s({filtersOpen:t.target.open})}> + ${this.host._t("device_inbox.filters_title")} + +
    + ${this.host._t("device_inbox.filters_excluded")} +
    + ${e.excluded.map(t=>p`${t} + + `)} +
    + {const i=t.target,o=i.value.trim();o&&!e.excluded.includes(o)&&s({draftExcluded:[...e.excluded,o]}),i.value=""}} /> + + ${o.map(t=>p``)} + + +
    + ${i?p`
    + ${this.host._t("device_inbox.filters_preview_appear",{count:String(i.appear)})} + · ${this.host._t("device_inbox.filters_preview_hide",{count:String(i.hide)})} + · ${this.host._t("device_inbox.filters_preview_lights",{count:String(i.lights)})} +
    `:d} + +
    `}_renderDeviceInbox(){const t=this.host._deviceInbox,e=this._deviceInboxRows(),s=Object.fromEntries(["on_plan","available","hidden","readd"].map(t=>[t,e.filter(e=>e.category===t).length])),i=function(t,e,s,i=!1){const o=s.trim().toLocaleLowerCase();return t.filter(t=>t.category===e&&(!i||t.isNew)&&(!o||t.searchText.includes(o)))}(e,t.tab,t.search,"on_plan"===t.tab&&t.onlyNew),o=i.slice(0,t.limit),a=t=>this.host._t(`device_inbox.tab_${t}`),n=`device_inbox.empty_${t.tab}`;return p`this.host._deviceInbox=null}> +
    +
    + this.host._deviceInbox={...t,search:e.target.value,limit:100}} /> + +
    +
    this._deviceInboxTabKey(t)}> + ${["on_plan","available","hidden","readd"].map(e=>p` + `)} +
    +
    + ${"on_plan"===t.tab?p``:d} + ${"available"===t.tab?p``:d} + + + ${this._help("device_inbox.show_hidden.help")} + +
    + ${"available"===t.tab?this._renderDiscoveryFilters(t):d} +
    + ${o.length?o.map(t=>{const e="on_plan"===t.category?t.canFind?p``:p``:"hidden"===t.category?p``:p``,s="active"===t.status.kind?"":this.host._t(`device_inbox.status_${t.status.kind}`);return p`
    + +
    +
    ${t.name} + ${t.isNew?p`${this.host._t("device_inbox.new")}`:d} +
    +
    + ${[t.model,t.integration,t.spaceName,t.areaName].filter(Boolean).join(" · ")} +
    +
    + ${"excluded_integration"===t.reason&&t.integration?this.host._t("device_inbox.reason_excluded_integration",{integration:t.integration}):this.host._t(`device_inbox.reason_${t.reason}`)} + ${s?p`${s}`:d} +
    + ${t.binding} +
    +
    + ${e} + ${t.canEdit||t.canHide||"available"===t.category||"hidden"===t.category||this.host._bindingHasHaPage(t.binding)?p` +
    + + + +
    + ${t.canEdit&&("on_plan"!==t.category||t.canFind)?p``:d} + ${t.canHide?p``:d} + ${"available"===t.category?p``:d} + ${"hidden"===t.category?p``:d} + ${this.host._bindingHasHaPage(t.binding)?p``:d} +
    +
    `:d} +
    +
    `}):p`
    ${this.host._t(n)}
    `} +
    + ${i.length>o.length?p``:d} +
    +
    + +
    +
    `}_markerValueBadgeFields(t){return Di({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerValueSourceFields(t){return Ci({touched:t.valueSourceTouched,originalHas:t.originalHasValueSource,original:t.originalValueSource,source:t.valueSource})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=zs(t.room),s=xs(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=Cs(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Oh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null},r=!!i&&("string"==typeof i.area&&i.area.length>0||null===i.area&&!!i.space&&!!i.room_id);return"virtual"===t.binding||t.roomTouched?(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null):r&&(n.space=i.space,n.area=i.area,n.room_id=i.room_id),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${_(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=Pi({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return Ii(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:Ps(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=Ms(this.host._planHass,i,o),n=Ds(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return Ti(zi(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&Ri(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&Ai(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of Ai(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${Ai(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>Fi(e)&&!i.has(e)&&Ni(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(Hi(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=Li(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),o=!e&&this.host._bindingHasHaPage(t.binding),a=this._markerPreviewDevice(t),n=this._effectiveMarkerTapAction(t,a),r=a?Es(this.host._serverCfg?.spaces.find(t=>t.id===a.space)):null,l=a?this._markerPreviewDevices(a):this.host._devices,h="toggle"===n&&a?this._toggleIntent(a,l):null,c=this._toggleHintLines(h),u=a?Ei(this.host._planHass,a,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:r?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(a.id),lightDevices:l,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,_=a?Ms(this.host._planHass,a,l):[],m=a?Ds(this.host._planHass,a,_):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!u?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:u?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:u?.valueBadge?.position||t.valueBadgePosition,v=Oi(f),y=!!f&&!_.some(t=>t.key===v),$=_.find(t=>t.key===v),w=Oi(t.valueSource),k=!(!t.valueSource||w&&_.some(t=>t.key===w)),x=_.find(t=>t.key===w),S=u?.valueSource?.sourceKey||"",M=this._markerAutoHasSpatialSource(t),D=!!a&&Bi(this.host._planHass,{...a}),C=Wi(t.lightRole,M,D,t.glowMode),P=!C.sourceExists,I=!C.fromSourceEnabled,T=C.passive,z=C.effectiveMode,R="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||M?T?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),A=a?Ai(a):[],F=a&&ji(a)||"",N=!!t.lightEntity&&!A.includes(t.lightEntity),H=a?Gi(a):[],L=!!t.toggleEntity&&!H.includes(t.toggleEntity),E="toggle"===n?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,O=E?[...E.targets,...E.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",B=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return p`this._closeMarkerDialog()}> +
    + ${"ha_disabled"===i?.kind?p`
    + + ${this.host._t(`marker.ha_disabled_${i.reason}`)} + ${o?p``:d} +
    `:"unverified"===i?.kind&&t.binding?p`
    + + ${this.host._t("marker.ha_registry_limited")} +
    `:d} + + this.host._markerDialog={...t,name:e.target.value}} /> + + +
    + +
    + + +
    + ${"ha"===t.bindingMode?p` + ${t.bindingOpen?p`
    + this.host._markerDialog={...t,bindingFilter:e.target.value}} /> +
    + ${s.map(e=>p`
    {if(e.value===t.binding)return void(this.host._markerDialog={...t,bindingOpen:!1});const s={...t,binding:e.value,bindingOpen:!1,controls:Cs(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value),valueSource:null,valueSourceTouched:!0})}}> + ${e.label}${e.sub} +
    `)} + ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} +
    +
    `:d}`:d} +
    + + + + + ${this._renderVacSection(t)} + + + + ${"toggle"===n&&(H.length>1||L)?p`
    +
    + + ${this._help("marker.toggle_entity.help")} +
    + + ${L?p`

    + + ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:O||this.host._t("marker.toggle_entity_none")})} +

    `:d} +
    `:d} + ${"toggle"===n?p`
    + ${c.map(t=>p`
    ${t}
    `)} +
    +
    ${t.tapHintAnnouncement}
    `:d} + ${"run"===n?(()=>{const e=t.runFilter.trim().toLowerCase(),s=this._runCandidates().filter(t=>!e||t.label.toLowerCase().includes(e)||t.value.includes(e)),i=t.tapTarget?this._runCandidates().find(e=>e.value===t.tapTarget):null;return p` + + ${t.tapTarget&&!i?p`
    ${this.host._t("marker.run_target_gone",{id:t.tapTarget})}
    `:d} + {t.target.select()}} + @input=${e=>this.host._markerDialog={...t,runFilter:e.target.value,tapTarget:""}} /> + ${i?d:p`
    + ${s.slice(0,40).map(e=>p`
    this.host._markerDialog={...t,tapTarget:e.value,runFilter:""}}> + ${e.label}${e.sub} +
    `)} + ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} +
    `}`})():d} + ${"run"===n||"toggle"===n?p``:d} + +
    + + ${this._help("marker.controls.help")} +
    + ${t.controls.length?p`
    + ${t.controls.map(e=>{const s=this._controlRefInfo(e);return p` + ${s.label} + this.host._markerDialog=this._announceToggleDraft({...t,controls:t.controls.filter(t=>t!==e)})}> + `})} +
    `:d} + this.host._markerDialog={...t,controlsFilter:e.target.value}} /> + ${t.controlsFilter.trim()?p`
    + ${this._controlCandidates(t).map(e=>p``)} +
    `:d} + + ${this.host._bindingHasClimate(t.binding)?p``:d} +
    + ${this.host._t("marker.light_role_label")}${this._help("marker.light_role.help")} +
    + + + +
    +
    + + ${"always"===t.lightRole&&(A.length>1||N)?p`
    +
    + + ${this._help("marker.light_entity.help")} +
    + + ${N?p`

    + + ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:F||"—"})} +

    `:d} +
    `:d} + +
    + ${this.host._t("marker.glow_color_label")}${this._help("marker.glow_mode.help")} +
    + + + +
    + ${"auto"!==z?p`
    + {this.host._markerDialog={...t,glowMode:z,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> + ${"fixed"===z?p` + ${this.host._t("marker.glow_brightness")} + ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:z,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},P,this.host._t("marker.glow_brightness"))} + ${Math.round(t.glowBrightness)}%`:d} +
    `:d} +
    +
    +
    + + ${this._help("marker.glow_radius.help")} +
    +
    + this.host._markerDialog={...t,glowRadius:e.target.value}} /> + ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} +
    +
    + ${P||T?p`

    + ${R} +

    `:d} + + + ${customElements.get("ha-icon-picker")?p`{const s=e.detail.value||"";(t.icon||s!==t.autoIcon)&&(this.host._markerDialog={...t,icon:s})}}>`:p`this.host._markerDialog={...t,icon:e.target.value}} />`} + ${!t.icon&&t.autoIcon?p`

    + ${this.host._t("marker.icon_auto",{icon:t.autoIcon})} +

    `:d} + + + +

    ${this.host._t(Hh[t.display])}

    + ${"value"===t.display?p`
    +
    + + ${this._help("marker.value_source.help")} +
    + + ${x?p`

    ${x.technical}

    `:d} + ${e?p`

    + + ${this.host._t("marker.preview.reason.value_virtual")} +

    `:k?p`

    + ${this.host._t("marker.value_source_missing_hint")} +

    `:d} +
    `:d} + ${"static_icon"===t.display&&this.host._bindingHasAlarm(t.binding)?p`
    + + ${this.host._t("marker.static_alarm_warning")} +
    `:d} +
    + ${this.host._t("marker.value_badge_title")}${this._help("marker.value_badge.help")} + + ${"static_icon"===t.display?p`

    + ${this.host._t("marker.value_badge_static")} +

    `:_.length||t.valueBadgeSource?d:p`

    + ${this.host._t("marker.value_badge_empty")} +

    `} + ${g?p` +
    + + ${this._help("marker.value_badge_source.help")} +
    + + ${$?p`

    ${$.technical}

    `:d} + ${y?p`

    + ${this.host._t("marker.value_badge_missing_hint")} +

    `:d} + ${"value"===t.display&&v===S?p`

    + ${this.host._t("marker.value_badge_duplicate")} +

    `:d} +
    + + ${this._help("marker.value_badge_position.help")} +
    + + `:d} +
    + ${u?p` + `:p`
    + + ${this.host._t("marker.preview.select_source")} +
    `} + ${"icon_ripple"===t.display?p`
    + {this.host._markerDialog={...t,rippleColor:e.detail.color}}}> +
    +
    + ${this.host._t("marker.ripple_size")} + ${this._rangeInput(1,8,.5,t.rippleSize,e=>this.host._markerDialog={...t,rippleSize:e})} + ×${t.rippleSize} +
    +

    ${this.host._t("marker.activity_alarm_note")}

    `:d} + + +
    + ${this._rangeInput(.5,3,.1,t.size,e=>this.host._markerDialog={...t,size:e})} + ×${t.size.toFixed(1)} + ${this.host._t("marker.angle_label")} + ${""} + ${this._rangeInput(0,355,5,t.angle,e=>this.host._markerDialog={...t,angle:e})} + ${t.angle}° +
    + + + this.host._markerDialog={...t,model:e.target.value}} /> + + + this.host._markerDialog={...t,link:e.target.value}} /> + + + + + +
    + ${t.pdfs.map(t=>p` + ${t.name} + this._removeMarkerPdf(t.url)}>`)} + + + this._pickMarkerFiles(t)} /> + +
    +
    +
    +
    + ${t.devId?p``:d} + ${t.devId?p``:d} +
    +
    + + +
    +
    +
    `}_renderSpaceDialog(){const t=this.host._spaceDialog,e=this.host._importTotal>0&&"create"===t.mode?this.host._t("import.progress",{i:this.host._importTotal-this.host._importQueue.length,n:this.host._importTotal}):"",s=()=>{this.host._spaceDialog=null,this.host._importQueue=[],this.host._importTotal=0};return p` +
    + + this.host._spaceDialog={...t,title:e.target.value}} /> + + + ${"file"===t.source?p`
    + ${t.planFile?p`${t.planFile.name}`:t.planUrl?p`${this.host._t("space.plan_alt")}`:p`${this.host._t("space.no_plan")}`} + + + this._pickPlanFile(t)} /> + + +
    + ${t.pickSaved?this._renderServerPlans(t):d}`:d} + + +
    + + ${this._help("space.cell_cm.help")} +
    +
    + {const s=e.target.value,i=Oh(s),o=null==i?null:Ji(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(Eh,o)):t.cellCm}}} /> + ${this.host._t(this.host._imperial?"space.scale_unit_imperial":"space.scale_unit")} +
    + + + +
    + + ${this._help("space.zero_wall_style.help")} +
    + + + + ${""} + +
    ${this.host._t("space.hide_decor_tip")}
    + +
    ${this.host._t("space.hide_openings_tip")}
    + + ${[["labelTemp","space.label_temp"],["labelHum","space.label_hum"],["labelLqi","space.label_lqi"],["labelLight","space.label_light"]].map(([e,s])=>p``)} + +
    + ${this._rangeInput(50,300,5,Math.round(100*t.cardFontScale),e=>this.host._spaceDialog={...t,cardFontScale:e/100})} + ${Math.round(100*t.cardFontScale)}% +
    + ${this.host._renderCardPreview(t.cardFontScale,1,1)} +
    + {this.host._spaceDialog={...t,roomColor:e.detail.color,roomOpacity:e.detail.opacity}}}> +
    +
    + + ${this._help("space.bg_mode.help")} +
    + + ${"static"===(t.bgMode??ae(this.host._settings,{}))?p`
    + {this.host._spaceDialog={...t,bgColor:e.detail.color}}}> + ${t.bgColor?p``:p`${this.host._t("space.bg_inherited")}`} +
    `:d} +
    + + ${this._help("space.north.help")} +
    +
    + {const s=e.target.value.trim(),i=""===s?null:Math.round(Number(s));this.host._spaceDialog={...t,northDeg:null!==i&&Number.isFinite(i)?Math.min(359,Math.max(0,i)):null}}} /> + ${null===t.northDeg?this.host._t("space.north_inherited",{v:null===ne(this.host._settings,{})?"—":String(ne(this.host._settings,{}))+"°"}):"°"} +
    + + +
    + ${this.host._t("space.fill_label")} + ${this._help("space.fill_mode.help")} +
    + ${Yi.map(t=>[t,"fill."+t]).map(([e,s])=>p` + ${"custom"===e&&"custom"===t.fillMode?p`
    + ${this.host._t("space.custom_fill")} + {this.host._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}> + ${t.customFill?p``:d} +
    `:d}`)} + + ${t.deleteBlockers?p``:d} +
    + +
    `}_renderMergeDialog(){const t=this.host._mergeDialog,e=this.host._spaceModel()?.rooms||[],s=(s,i)=>{const o=e.find(t=>t.id===s),a=o?.area?this.host.hass.areas[o.area]?.name:null;return p``};return p`this.host._mergeDialog=null}> +
    +

    ${this.host._t("merge.hint")}

    + + ${s(t.aId,"a")} + ${s(t.bId,"b")} +
    +
    + + + +
    +
    `}_renderRoomSource(t){const e="temp"===t?this.host._roomTempSrc:this.host._roomHumSrc,s=e=>{"temp"===t?this.host._roomTempSrc=e:this.host._roomHumSrc=e,this.host.requestUpdate()},i=this.host._roomSrcOpen===t;return p` + + + + ${e||i?p` + ${i?p`
    + {this.host._roomSrcFilter=t.target.value,this.host.requestUpdate()}} /> +
    + ${this._roomSrcCandidates().map(t=>p`
    {s(t.value),this.host._roomSrcOpen=null}}> + ${t.label}${t.sub} +
    `)} +
    +
    `:d}`:d}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),o=Es(this.host._curSpaceCfg),a=this.host._roomFill||o.fill,n=this.host._roomCustomFill||o.customFill,r=[...this.host._freeAreas];if(t&&this.host._areaSel&&!r.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&r.unshift(t)}return p`this._roomDialogCancel()}> +
    + ${s?p`

    + ${s} +

    `:d} + + this.host._nameSel=t.target.value} /> + + + + + + ${[["","fill.inherit"],...Xi.map(t=>[t,"fill."+t])].map(([t,e])=>p``)} + ${"custom"===a?p`
    + ${this.host._roomCustomFill?this.host._t("room.custom_fill_own"):this.host._t("room.custom_fill_space")} + {this.host._roomCustomFill={c:t.detail.color,a:t.detail.opacity}}}> + ${this.host._roomCustomFill?p``:d} +
    `:d} + ${this._renderRoomSource("temp")} + ${this._renderRoomSource("hum")} + + + +
    + ${this._rangeInput(50,300,5,Math.round(100*this.host._roomNameScale),t=>{this.host._roomNameScale=t/100,this.host.requestUpdate()})} + ${Math.round(100*this.host._roomNameScale)}% +
    + +
    + ${this._rangeInput(50,300,5,Math.round(100*this.host._roomLabelScale),t=>{this.host._roomLabelScale=t/100,this.host.requestUpdate()})} + ${Math.round(100*this.host._roomLabelScale)}% +
    + ${this.host._renderCardPreview(Es(this.host._curSpaceCfg).cardFontScale,this.host._roomNameScale,this.host._roomLabelScale)} +
    +
    + + + ${t?p``:p`${this.host._pendingSplit?d:p``} + `} +
    +
    `}}export{qh as EDITOR_RUNTIME_FINGERPRINT,Uh as HouseplanEditorRuntime}; diff --git a/dist/houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js b/dist/houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js deleted file mode 100644 index 0fd818b4..00000000 --- a/dist/houseplan-assets/houseplan-editor-runtime-CaF5LJAh.js +++ /dev/null @@ -1,2911 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{e as t,i as e,E as s,s as i,a as o,n as a,f as n,b as r,l,o as h,r as c,A as d,c as p,d as u,g as _,h as m,j as g,k as f,m as b,p as v,t as y,w as $,q as w,u as k,v as x,x as S,y as M,z as D,B as C,C as P,D as I,F as T,G as R,H as z,I as A,J as F,K as N,L as H,M as L,N as E,O,P as B,Q as W,R as j,S as G,T as q,U,V as K,W as V,X as J,Y,Z,_ as X,$ as Q,a0 as tt,a1 as et,a2 as st,a3 as it,a4 as ot,a5 as at,a6 as nt,a7 as rt,a8 as lt,a9 as ht,aa as ct,ab as dt,ac as pt,ad as ut,ae as _t,af as mt,ag as gt,ah as ft,ai as bt,aj as vt,ak as yt,al as $t,am as wt,an as kt,ao as xt,ap as St,aq as Mt,ar as Dt,as as Ct,at as Pt,au as It,av as Tt,aw as Rt,ax as zt,ay as At,az as Ft,aA as Nt,aB as Ht,aC as Lt,aD as Et,aE as Ot,aF as Bt,aG as Wt,aH as jt,aI as Gt,aJ as qt,aK as Ut,aL as Kt,aM as Vt,aN as Jt,aO as Yt,aP as Zt,aQ as Xt,aR as Qt,aS as te,aT as ee,aU as se,aV as ie,aW as oe,aX as ae,aY as ne,aZ as re,a_ as le,a$ as he,b0 as ce,b1 as de,b2 as pe,b3 as ue,b4 as _e,b5 as me,b6 as ge,b7 as fe,b8 as be,b9 as ve,ba as ye,bb as $e,bc as we,bd as ke,be as xe,bf as Se,bg as Me,bh as De,bi as Ce,bj as Pe,bk as Ie,bl as Te,bm as Re,bn as ze,bo as Ae,bp as Fe,bq as Ne,br as He,bs as Le,bt as Ee,bu as Oe,bv as Be,bw as We,bx as je,by as Ge,bz as qe,bA as Ue,bB as Ke,bC as Ve,bD as Je,bE as Ye,bF as Ze,bG as Xe,bH as Qe,bI as ts,bJ as es,bK as ss,bL as is,bM as os,bN as as,bO as ns,bP as rs,bQ as ls,bR as hs,bS as cs,bT as ds,bU as ps,bV as us,bW as _s,bX as ms,bY as gs,bZ as fs,b_ as bs,b$ as vs,c0 as ys,c1 as $s,c2 as ws,c3 as ks,c4 as xs,c5 as Ss,c6 as Ms,c7 as Ds,c8 as Cs,c9 as Ps,ca as Is,cb as Ts,cc as Rs,cd as zs,ce as As,cf as Fs,cg as Ns,ch as Hs,ci as Ls,cj as Es,ck as Os,cl as Bs,cm as Ws,cn as js,co as Gs,cp as qs,cq as Us,cr as Ks,cs as Vs,ct as Js,cu as Ys,cv as Zs,cw as Xs,cx as Qs,cy as ti,cz as ei,cA as si,cB as ii,cC as oi,cD as ai,cE as ni,cF as ri,cG as li,cH as hi,cI as ci,cJ as di,cK as pi,cL as ui,cM as _i,cN as mi,cO as gi,cP as fi,cQ as bi,cR as vi,cS as yi,cT as $i,cU as wi,cV as ki,cW as xi,cX as Si,cY as Mi,cZ as Di,c_ as Ci,c$ as Pi,d0 as Ii,d1 as Ti,d2 as Ri,d3 as zi,d4 as Ai,d5 as Fi,d6 as Ni,d7 as Hi,d8 as Li,d9 as Ei,da as Oi,db as Bi,dc as Wi,dd as ji,de as Gi,df as qi,dg as Ui,dh as Ki,di as Vi,dj as Ji,dk as Yi,dl as Zi,dm as Xi}from"./houseplan-card-DcH_msOh.js";import{F as Qi,f as to,p as eo,c as so,i as io,e as oo,r as ao,a as no,b as ro,s as lo,d as ho,t as co}from"./backdrop-pick-CXzP7hUG.js";import{t as po,m as uo,T as _o}from"./zigbee-topology-x104QxRs.js";const mo={},go=t(class extends e{constructor(){super(...arguments),this.ot=mo}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return s}else if(this.ot===e)return s;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}});var fo={"gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.show_room_tooltip":"Show the room information window on hover","support.about_group":"About the card","support.guide_group":"Documentation","support.guide":"User guide","support.form_group":"Send a report or suggestion","support.contact":"Contact details (email/tg/WhatsApp), optional.","support.message":"Message","support.attach":"Attach anonymized information from your plan","support.attach_hint":"Includes only allowed plan geometry, settings and bounded runtime facts. Excludes names, Home Assistant IDs, live states, URLs, files, and your message/contact.","support.geometry_warning":"The package contains no names or Home Assistant IDs, but it does contain the exact geometry and dimensions of your home.","support.close_busy_title":"Stop the current operation?","support.close_busy_body":"House Plan is preparing or sending the report. Closing now stops waiting for the result.","support.building":"Preparing anonymized data…","support.preview_summary":"Package v{version} · {spaces} spaces · {size} KiB","support.prepared":"Snapshot prepared {n} min ago","support.show_data":"Show data","support.download":"Download JSON","support.refresh":"Refresh snapshot","support.privacy":"When you send, your message, optional contact and, if selected, exact geometry leave your Home Assistant through the House Plan project relay and are visible to maintainers. The project node keeps the report for up to 30 days. Network infrastructure sees your HA server address, but House Plan does not retain it raw; rate-limit and idempotency metadata is kept for up to 24 hours.","support.error_title":"The report was not sent","support.manual_recovery":"You can keep the message and attachment and continue manually:","support.copy_message":"Copy message","support.success":"Report sent: {id}. Save this number when contacting support.","support.copy_id":"Copy ID","support.sending":"Sending…","support.retry":"Retry","support.send":"Send","support.update_required":"Update the House Plan card and integration to compatible versions to send reports.","support.message_copied":"Message copied","support.id_copied":"Report ID copied","support.copy_failed":"Could not access the clipboard. Select and copy the text manually.","support.validation.message_required":"Enter a message.","support.validation.message_too_long":"The message must contain no more than 10,000 characters.","support.validation.contact_too_long":"Contact details must contain no more than 320 characters.","support.validation.preview_missing":"Wait for the attachment preview or turn the attachment off.","support.validation.preview_expired":"The attachment preview expired. Refresh the snapshot.","support.error.support_invalid_message":"Check the message and try again.","support.error.support_preview_expired":"The attachment preview expired or is no longer available. Refresh the snapshot.","support.error.support_package_too_large":"The anonymized package exceeds the 8 MiB limit.","support.error.support_rate_limited":"Too many reports or previews were requested. Try again later.","support.error.support_unavailable":"The private support service is temporarily unavailable. Your draft remains here.","support.error.support_rejected":"The report did not pass the support service validation.","support.error.unauthorized":"You do not have permission to send this report.","vac.routes_section":"Maps and floors","vac.routes_current_map":"Current map","vac.routes_no_map":"no telemetry","vac.routes_status":"Routing","vac.routes_state_ready":"map assigned and calibrated","vac.routes_state_needs_calibration":"map assigned, calibration needed","vac.routes_state_unmapped":"current map is not assigned to a floor","vac.routes_state_ambiguous":"two maps are indistinguishable — no floor is chosen","vac.routes_state_missing_space":"the map's space was deleted","vac.routes_state_none":"no map data","vac.routes_empty":"No map is assigned to a floor yet.","vac.route_map_default":"no map id","vac.route_active":"Active","vac.route_space":"Space","vac.route_space_none":"not selected","vac.route_status_ready":"ready","vac.route_status_needs_calibration":"needs calibration","vac.route_status_ambiguous":"ambiguous","vac.route_status_missing_space":"space deleted","vac.route_missing_space_group":"Deleted space","vac.route_add_current":"Add current map","vac.route_delete":"Remove map","vac.route_delete_title":"Remove the map from its floor?","vac.route_delete_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_delete_confirm":"Remove","vac.route_space_title":"Move the map to another space?","vac.route_space_body":"Calibration and saved paths for this map will be removed. Continue?","vac.route_space_confirm":"Move","vac.route_missing_space_hint":"One of the maps points at a deleted space: pick a new one or remove the map. Until then the robot is not drawn for that map.","vac.route_default_hint":"The source reports no stable map id. Several floors cannot be told apart without another source or the robot's own selected map.","vac.route_target_hint":"The dock stays in the “{space}” space regardless of the current map.","vac.route_add_source":"Add a map source","vac.route_add_source_hint":"For integrations that give each map its own camera. The “All cameras” section above widens this list.","vac.route_source_no_map":"no map reported"};const bo={en:fo,ru:{"gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.show_room_tooltip":"Показывать окно с информацией при наведении на комнату","support.about_group":"О карточке","support.guide_group":"Документация","support.guide":"Руководство пользователя","support.form_group":"Отправить репорт или предложение","support.contact":"Контакт для связи (email/tg/WhatsApp), необязательно.","support.message":"Сообщение","support.attach":"Прикрепить обезличенную информацию из вашего плана","support.attach_hint":"В пакет входят только разрешённые геометрия, настройки и ограниченные сведения о среде. Не входят имена, HA id, текущие состояния, URL, файлы, сообщение и контакт.","support.geometry_warning":"Пакет не содержит имён и HA id, но содержит точную геометрию и размеры дома.","support.close_busy_title":"Прервать текущую операцию?","support.close_busy_body":"House Plan подготавливает или отправляет репорт. При закрытии ожидание результата прекратится.","support.building":"Подготавливаем обезличенные данные…","support.preview_summary":"Пакет v{version} · пространств: {spaces} · {size} КиБ","support.prepared":"Снимок подготовлен {n} мин назад","support.show_data":"Показать данные","support.download":"Скачать JSON","support.refresh":"Обновить снимок","support.privacy":"При отправке сообщение, необязательный контакт и, если выбран пакет, точная геометрия покидают ваш Home Assistant через relay проекта House Plan и доступны мейнтейнерам. Узел проекта хранит репорт до 30 дней. Сетевая инфраструктура видит адрес сервера HA, но House Plan не сохраняет его в исходном виде; метаданные лимитов и идемпотентности хранятся до 24 часов.","support.error_title":"Репорт не отправлен","support.manual_recovery":"Сообщение и вложение можно сохранить и продолжить вручную:","support.copy_message":"Копировать сообщение","support.success":"Репорт отправлен: {id}. Сохраните номер для связи с поддержкой.","support.copy_id":"Копировать ID","support.sending":"Отправляем…","support.retry":"Повторить","support.send":"Отправить","support.update_required":"Обновите карточку и интеграцию House Plan до совместимых версий, чтобы отправлять репорты.","support.message_copied":"Сообщение скопировано","support.id_copied":"ID репорта скопирован","support.copy_failed":"Не удалось получить доступ к буферу обмена. Выделите и скопируйте текст вручную.","support.validation.message_required":"Введите сообщение.","support.validation.message_too_long":"Сообщение должно содержать не более 10 000 символов.","support.validation.contact_too_long":"Контакт должен содержать не более 320 символов.","support.validation.preview_missing":"Дождитесь предпросмотра вложения или отключите его.","support.validation.preview_expired":"Предпросмотр вложения истёк. Обновите снимок.","support.error.support_invalid_message":"Проверьте сообщение и повторите попытку.","support.error.support_preview_expired":"Предпросмотр вложения истёк или больше недоступен. Обновите снимок.","support.error.support_package_too_large":"Обезличенный пакет превышает лимит 8 МиБ.","support.error.support_rate_limited":"Запрошено слишком много репортов или предпросмотров. Повторите позже.","support.error.support_unavailable":"Закрытая служба поддержки временно недоступна. Черновик сохранён в этом окне.","support.error.support_rejected":"Репорт не прошёл проверку службы поддержки.","support.error.unauthorized":"У вас нет прав на отправку этого репорта.","vac.routes_section":"Карты и этажи","vac.routes_current_map":"Текущая карта","vac.routes_no_map":"источник молчит","vac.routes_status":"Маршрутизация","vac.routes_state_ready":"карта сопоставлена и откалибрована","vac.routes_state_needs_calibration":"карта сопоставлена, нужна калибровка","vac.routes_state_unmapped":"текущая карта не сопоставлена этажу","vac.routes_state_ambiguous":"две карты неразличимы — этаж не выбирается","vac.routes_state_missing_space":"пространство карты удалено","vac.routes_state_none":"нет данных о карте","vac.routes_empty":"Ни одна карта ещё не сопоставлена этажу.","vac.route_map_default":"без идентификатора","vac.route_active":"Текущая","vac.route_space":"Пространство","vac.route_space_none":"не выбрано","vac.route_status_ready":"готова","vac.route_status_needs_calibration":"нужна калибровка","vac.route_status_ambiguous":"неоднозначна","vac.route_status_missing_space":"пространство удалено","vac.route_missing_space_group":"Пространство удалено","vac.route_add_current":"Добавить текущую карту","vac.route_delete":"Удалить карту","vac.route_delete_title":"Удалить карту с этажа?","vac.route_delete_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_delete_confirm":"Удалить","vac.route_space_title":"Перенести карту в другое пространство?","vac.route_space_body":"Калибровка и сохранённые пути этой карты будут удалены. Продолжить?","vac.route_space_confirm":"Перенести","vac.route_missing_space_hint":"Одна из карт ссылается на удалённое пространство: выберите новое или удалите карту. Пока ссылка не исправлена, робот на этой карте не рисуется.","vac.route_default_hint":"Источник не сообщает стабильный идентификатор карты. Несколько этажей нельзя различить без другого источника или собственной карты робота.","vac.route_target_hint":"Док остаётся в пространстве «{space}» независимо от текущей карты.","vac.route_add_source":"Добавить источник карты","vac.route_add_source_hint":"Для интеграций, где у каждой карты своя камера. Список расширяется разделом «Все камеры» выше.","vac.route_source_no_map":"карта не читается"},de:{"gs.hint":"Füllfarben gelten für jeden Bereich; jede Farbe hat ihre eigene Deckkraft. Der Füllmodus wird in den Einstellungen des jeweiligen Bereichs festgelegt.","gs.show_room_tooltip":"Rauminformationen beim Darüberfahren anzeigen","support.about_group":"Über die Karte","support.guide_group":"Dokumentation","support.guide":"Benutzerhandbuch","support.form_group":"Bericht oder Vorschlag senden","support.contact":"Kontakt (E-Mail/tg/WhatsApp), optional.","support.message":"Nachricht","support.attach":"Anonymisierte Informationen aus dem Plan anhängen","support.attach_hint":"Enthält nur erlaubte Plangeometrie, Einstellungen und begrenzte Laufzeitdaten. Namen, Home-Assistant-IDs, Live-Zustände, URLs, Dateien, Nachricht und Kontakt werden ausgeschlossen.","support.geometry_warning":"Das Paket enthält keine Namen oder Home-Assistant-IDs, aber die exakte Geometrie und die Maße Ihres Hauses.","support.close_busy_title":"Aktuellen Vorgang abbrechen?","support.close_busy_body":"House Plan bereitet den Bericht vor oder sendet ihn. Beim Schließen wird nicht weiter auf das Ergebnis gewartet.","support.building":"Anonymisierte Daten werden vorbereitet…","support.preview_summary":"Paket v{version} · {spaces} Bereiche · {size} KiB","support.prepared":"Schnappschuss vor {n} Min. erstellt","support.show_data":"Daten anzeigen","support.download":"JSON herunterladen","support.refresh":"Schnappschuss aktualisieren","support.privacy":"Beim Senden verlassen Ihre Nachricht, der optionale Kontakt und, falls ausgewählt, die exakte Geometrie Ihren Home Assistant über das House-Plan-Projektrelay und sind für die Maintainer sichtbar. Der Projektserver speichert den Bericht bis zu 30 Tage. Die Netzwerkinfrastruktur sieht die Adresse Ihres HA-Servers, House Plan speichert sie jedoch nicht im Rohformat; Limit- und Idempotenzdaten bleiben bis zu 24 Stunden erhalten.","support.error_title":"Der Bericht wurde nicht gesendet","support.manual_recovery":"Nachricht und Anhang können gespeichert und manuell weitergegeben werden:","support.copy_message":"Nachricht kopieren","support.success":"Bericht gesendet: {id}. Bewahren Sie diese Nummer für den Support auf.","support.copy_id":"ID kopieren","support.sending":"Wird gesendet…","support.retry":"Erneut versuchen","support.send":"Senden","support.update_required":"Aktualisieren Sie House-Plan-Karte und Integration auf kompatible Versionen, um Berichte zu senden.","support.message_copied":"Nachricht kopiert","support.id_copied":"Berichts-ID kopiert","support.copy_failed":"Kein Zugriff auf die Zwischenablage. Markieren und kopieren Sie den Text manuell.","support.validation.message_required":"Geben Sie eine Nachricht ein.","support.validation.message_too_long":"Die Nachricht darf höchstens 10.000 Zeichen enthalten.","support.validation.contact_too_long":"Der Kontakt darf höchstens 320 Zeichen enthalten.","support.validation.preview_missing":"Warten Sie auf die Vorschau oder deaktivieren Sie den Anhang.","support.validation.preview_expired":"Die Anhangsvorschau ist abgelaufen. Aktualisieren Sie den Schnappschuss.","support.error.support_invalid_message":"Prüfen Sie die Nachricht und versuchen Sie es erneut.","support.error.support_preview_expired":"Die Anhangsvorschau ist abgelaufen oder nicht mehr verfügbar. Aktualisieren Sie den Schnappschuss.","support.error.support_package_too_large":"Das anonymisierte Paket überschreitet das Limit von 8 MiB.","support.error.support_rate_limited":"Zu viele Berichte oder Vorschauen wurden angefordert. Versuchen Sie es später erneut.","support.error.support_unavailable":"Der private Supportdienst ist vorübergehend nicht verfügbar. Der Entwurf bleibt in diesem Fenster erhalten.","support.error.support_rejected":"Der Bericht hat die Prüfung des Supportdienstes nicht bestanden.","support.error.unauthorized":"Sie dürfen diesen Bericht nicht senden.","vac.routes_section":"Karten und Etagen","vac.routes_current_map":"Aktuelle Karte","vac.routes_no_map":"keine Telemetrie","vac.routes_status":"Zuordnung","vac.routes_state_ready":"Karte zugeordnet und kalibriert","vac.routes_state_needs_calibration":"Karte zugeordnet, Kalibrierung nötig","vac.routes_state_unmapped":"aktuelle Karte keiner Etage zugeordnet","vac.routes_state_ambiguous":"zwei Karten nicht unterscheidbar — keine Etage gewählt","vac.routes_state_missing_space":"der Raum der Karte wurde gelöscht","vac.routes_state_none":"keine Kartendaten","vac.routes_empty":"Noch keine Karte einer Etage zugeordnet.","vac.route_map_default":"ohne Karten-ID","vac.route_active":"Aktiv","vac.route_space":"Raum","vac.route_space_none":"nicht gewählt","vac.route_status_ready":"bereit","vac.route_status_needs_calibration":"Kalibrierung nötig","vac.route_status_ambiguous":"mehrdeutig","vac.route_status_missing_space":"Raum gelöscht","vac.route_missing_space_group":"Gelöschter Bereich","vac.route_add_current":"Aktuelle Karte hinzufügen","vac.route_delete":"Karte entfernen","vac.route_delete_title":"Karte von der Etage entfernen?","vac.route_delete_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_delete_confirm":"Entfernen","vac.route_space_title":"Karte in einen anderen Raum verschieben?","vac.route_space_body":"Kalibrierung und gespeicherte Pfade dieser Karte werden entfernt. Fortfahren?","vac.route_space_confirm":"Verschieben","vac.route_missing_space_hint":"Eine Karte verweist auf einen gelöschten Raum: neuen wählen oder Karte entfernen. Bis dahin wird der Roboter für diese Karte nicht gezeichnet.","vac.route_default_hint":"Die Quelle meldet keine stabile Karten-ID. Mehrere Etagen lassen sich ohne andere Quelle oder die vom Roboter gewählte Karte nicht unterscheiden.","vac.route_target_hint":"Die Station bleibt unabhängig von der aktuellen Karte im Raum „{space}“.","vac.route_add_source":"Kartenquelle hinzufügen","vac.route_add_source_hint":"Für Integrationen, die jeder Karte eine eigene Kamera geben. Der Abschnitt „Alle Kameras“ oben erweitert diese Liste.","vac.route_source_no_map":"keine Karte gemeldet"},fr:{"gs.hint":"Les couleurs de remplissage s’appliquent à tous les espaces ; chaque couleur possède sa propre opacité. Le mode de remplissage utilisé par un espace se règle dans la boîte de dialogue de cet espace.","gs.show_room_tooltip":"Afficher les informations de la pièce au survol","support.about_group":"À propos de la carte","support.guide_group":"Documentation d’aide","support.guide":"Guide utilisateur","support.form_group":"Envoyer un rapport ou une suggestion","support.contact":"Contact (e-mail/tg/WhatsApp), facultatif.","support.message":"Votre message","support.attach":"Joindre des informations anonymisées de votre plan","support.attach_hint":"Inclut uniquement la géométrie, les réglages autorisés et des données d’exécution limitées. Exclut les noms, identifiants Home Assistant, états en direct, URL, fichiers, message et contact.","support.geometry_warning":"Le paquet ne contient ni noms ni identifiants Home Assistant, mais il contient la géométrie et les dimensions exactes de votre logement.","support.close_busy_title":"Interrompre l’opération en cours ?","support.close_busy_body":"House Plan prépare ou envoie le rapport. Fermer maintenant arrête l’attente du résultat.","support.building":"Préparation des données anonymisées…","support.preview_summary":"Paquet v{version} · {spaces} espaces · {size} Kio","support.prepared":"Instantané préparé il y a {n} min","support.show_data":"Afficher les données","support.download":"Télécharger le JSON","support.refresh":"Actualiser l’instantané","support.privacy":"Lors de l’envoi, votre message, le contact facultatif et, si elle est sélectionnée, la géométrie exacte quittent votre Home Assistant via le relais du projet House Plan et sont visibles par les mainteneurs. Le serveur du projet conserve le rapport jusqu’à 30 jours. L’infrastructure réseau voit l’adresse de votre serveur HA, mais House Plan ne la conserve pas en clair ; les métadonnées de limitation et d’idempotence sont gardées jusqu’à 24 heures.","support.error_title":"Le rapport n’a pas été envoyé","support.manual_recovery":"Vous pouvez conserver le message et la pièce jointe puis continuer manuellement :","support.copy_message":"Copier le message","support.success":"Rapport envoyé : {id}. Conservez ce numéro pour contacter l’assistance.","support.copy_id":"Copier l’ID","support.sending":"Envoi…","support.retry":"Réessayer","support.send":"Envoyer","support.update_required":"Mettez à jour la carte et l’intégration House Plan vers des versions compatibles pour envoyer des rapports.","support.message_copied":"Message copié","support.id_copied":"ID du rapport copié","support.copy_failed":"Impossible d’accéder au presse-papiers. Sélectionnez et copiez le texte manuellement.","support.validation.message_required":"Saisissez un message.","support.validation.message_too_long":"Le message ne doit pas dépasser 10 000 caractères.","support.validation.contact_too_long":"Le contact ne doit pas dépasser 320 caractères.","support.validation.preview_missing":"Attendez l’aperçu de la pièce jointe ou désactivez-la.","support.validation.preview_expired":"L’aperçu de la pièce jointe a expiré. Actualisez l’instantané.","support.error.support_invalid_message":"Vérifiez le message et réessayez.","support.error.support_preview_expired":"L’aperçu de la pièce jointe a expiré ou n’est plus disponible. Actualisez l’instantané.","support.error.support_package_too_large":"Le paquet anonymisé dépasse la limite de 8 Mio.","support.error.support_rate_limited":"Trop de rapports ou d’aperçus ont été demandés. Réessayez plus tard.","support.error.support_unavailable":"Le service d’assistance privé est temporairement indisponible. Le brouillon reste dans cette fenêtre.","support.error.support_rejected":"Le rapport n’a pas passé la validation du service d’assistance.","support.error.unauthorized":"Vous n’êtes pas autorisé à envoyer ce rapport.","vac.routes_section":"Cartes et étages","vac.routes_current_map":"Carte actuelle","vac.routes_no_map":"aucune télémétrie","vac.routes_status":"Routage","vac.routes_state_ready":"carte assignée et calibrée","vac.routes_state_needs_calibration":"carte assignée, calibrage requis","vac.routes_state_unmapped":"carte actuelle non assignée à un étage","vac.routes_state_ambiguous":"deux cartes indiscernables — aucun étage choisi","vac.routes_state_missing_space":"l'espace de la carte a été supprimé","vac.routes_state_none":"aucune donnée de carte","vac.routes_empty":"Aucune carte n'est encore assignée à un étage.","vac.route_map_default":"sans identifiant","vac.route_active":"Active","vac.route_space":"Espace","vac.route_space_none":"non sélectionné","vac.route_status_ready":"prête","vac.route_status_needs_calibration":"calibrage requis","vac.route_status_ambiguous":"ambiguë","vac.route_status_missing_space":"espace supprimé","vac.route_missing_space_group":"Espace supprimé","vac.route_add_current":"Ajouter la carte actuelle","vac.route_delete":"Supprimer la carte","vac.route_delete_title":"Retirer la carte de son étage ?","vac.route_delete_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_delete_confirm":"Supprimer","vac.route_space_title":"Déplacer la carte vers un autre espace ?","vac.route_space_body":"Le calibrage et les trajets enregistrés de cette carte seront supprimés. Continuer ?","vac.route_space_confirm":"Déplacer","vac.route_missing_space_hint":"Une carte pointe vers un espace supprimé : choisissez-en un autre ou supprimez la carte. D'ici là, le robot n'est pas dessiné pour cette carte.","vac.route_default_hint":"La source ne fournit pas d'identifiant de carte stable. Plusieurs étages ne peuvent pas être distingués sans une autre source ou la carte sélectionnée du robot.","vac.route_target_hint":"La station reste dans l'espace « {space} » quelle que soit la carte actuelle.","vac.route_add_source":"Ajouter une source de carte","vac.route_add_source_hint":"Pour les intégrations qui donnent à chaque carte sa propre caméra. La section « Toutes les caméras » ci-dessus élargit cette liste.","vac.route_source_no_map":"aucune carte signalée"}};function vo(t,e,s){return i(bo[t]?.[e]??fo[e]??e,s)}function yo(t,e,s,i,o){return{markerId:t,source:i,mapId:o,space:e.length?"":s}}function $o(t,e,s){return{...t,space:s.has(e)?e:""}}function wo(t,e=Math.random){const s=new Set(t);for(let t=0;t<64;t+=1){const t="vr_"+Math.floor(16777215*e()).toString(36).padStart(5,"0");if(!s.has(t))return t}return"vr_"+Date.now().toString(36)}function ko(t,e,s){return t.map(t=>t.id===e?{...t,calibration:s}:t)}const xo=new WeakMap,So=(t,e)=>{const s=(t._serverCfg?.spaces||[]).find(t=>t?.id===e);return String(s?.name||s?.id||e)};function Mo(t,e,s){const i=t.host,n=l(i.hass,i._config?.language),r=(t,e)=>vo(n,t,e),g=e.marker?.vacuum||{},f=Array.isArray(g.map_routes),b=i._vacSource(e)||"",v=o(e.id,g,e.space,b),y=(i._serverCfg?.spaces||[]).filter(t=>"string"==typeof t?.id&&!!t.id).map(t=>({id:t.id,name:String(t.name||t.id)})),$=new Set(y.map(t=>t.id)),w=h(v,[b],t=>i._vacObservedMapId(e,t)),k=c({routes:v,observed:w,spaceIds:$}),x="ready"===k.kind||"needs_calibration"===k.kind||"missing_space"===k.kind?k.route.id:"",S=b?w[b]:void 0,M=async s=>{let o=f?g.map_routes??null:null;if(!o&&(o=function(t,e,s,i){if(!s)return null;const o=t?.calibration;if(!o||"object"!=typeof o)return null;const n=new Set,r=[];for(const t of Object.keys(o)){const l=a(o[t]);if(!l)continue;const h=i(n);n.add(h),r.push({id:h,source:s,map_id:t,space:e,calibration:l})}return r.length?r:null}(g,e.space,b,t=>wo(t))??[],!b&&Object.keys(g.calibration||{}).length))return!1;const n=s(o);return!!n&&(async s=>{const o=i._serverCfg;if(!o)return!1;const a=JSON.parse(JSON.stringify(o));a.markers=a.markers||[];let n=a.markers.find(t=>t.id===e.id);if(!n){if("device"!==e.bindingKind&&"entity"!==e.bindingKind||!e.bindingRef)return!1;n={id:e.id,binding:`${e.bindingKind}:${e.bindingRef}`,space:e.space||null,area:e.area||null,hidden:!!e.hidden},a.markers.push(n)}n.vacuum={...n.vacuum||{},map_routes:s},delete n.vacuum.calibration;const r=u(o,a,i._cfgContentFingerprint,i._cfgRev,_);i._serverCfg=a,i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._saveConfigDebounced.pending()&&i._saveConfigDebounced.cancel();try{return await t._saveConfigNow(),!0}catch(t){return m(i,r,_),i._regSignature="",i._maybeRebuildDevices(),i.requestUpdate(),i._showToast(i._t("toast.cfg_save_failed",{err:i._errText(t)})),!1}})(n)},D=s.candidates.filter(t=>t.entityId!==b&&!v.some(e=>e.source===t.entityId)),C=async(t,e)=>{if(!e||e===t.space)return;await i._confirmDanger({key:"vacuum_route_space",kind:"warning",title:r("vac.route_space_title"),message:r("vac.route_space_body"),objectName:t.map_id||r("vac.route_map_default"),confirmLabel:r("vac.route_space_confirm"),cancelLabel:i._t("btn.cancel")})&&await M(s=>function(t,e,s,i){return t.map(t=>t.id===e?{...t,id:i,space:s,calibration:null}:t)}(s,t.id,e,wo(new Set(v.map(t=>t.id)))))},P=new Map(y.map((t,e)=>[t.id,e])),I=(t,e)=>t.map_id.localeCompare(e.map_id)||t.source.localeCompare(e.source)||t.id.localeCompare(e.id),T=v.filter(t=>$.has(t.space)).sort((t,e)=>(P.get(t.space)??0)-(P.get(e.space)??0)||I(t,e)),R=v.filter(t=>!$.has(t.space)).sort(I),z=[...T,...R],A=!!b&&void 0!==S&&!v.some(t=>t.source===b&&t.map_id===S);let F=xo.get(t);const N=F?.source||"",H=F?.mapId||"",L=!!F&&v.some(t=>t.source===N&&t.map_id===H);F&&(F.markerId!==e.id||i._vacObservedMapId(e,F.source)!==F.mapId||L&&!F.saving)&&(xo.delete(t),F=void 0),F?.space&&!$.has(F.space)&&(F=$o(F,"",$),xo.set(t,F));const E=s=>p` -
  • -
    - ${s.map_id||r("vac.route_map_default")} - ${s.id===x?p`${r("vac.route_active")}`:d} -
    - ${s.source} -
    ${(t=>$.has(t.space)?t.calibration?"ambiguous"===k.kind&&k.routeIds.includes(t.id)?r("vac.route_status_ambiguous"):r("vac.route_status_ready"):r("vac.route_status_needs_calibration"):r("vac.route_status_missing_space"))(s)}
    - -
    - - -
    -
  • `,O=`vacroute-missing-${e.id.replace(/[^a-zA-Z0-9_-]/g,"-")}`;return p` - -
    -
    -
    ${r("vac.routes_current_map")}${void 0===S?r("vac.routes_no_map"):S||r("vac.route_map_default")}
    -
    ${r("vac.routes_status")}${r(`vac.routes_state_${k.kind}`)}
    -
    - ${T.length||F?p`
      - ${F?p` -
    • -
      ${F.mapId||r("vac.route_map_default")}
      - ${F.source} - -
      - - -
      -
    • `:d} - ${T.map(E)} -
    `:d} - ${R.length?p` -
    -

    - ${r("vac.route_missing_space_group")} -

    -
      ${R.map(E)}
    -
    `:d} - ${z.length||F?d:p`
    ${r("vac.routes_empty")}
    `} -
    - -
    - ${D.length?p`
    - ${r("vac.route_add_source")} -
    ${r("vac.route_add_source_hint")}
    -
    - ${D.map(s=>{const o=i._vacObservedMapId(e,s.entityId);return p``})} -
    -
    `:d} - ${z.some(t=>!$.has(t.space))?p` -
    ${r("vac.route_missing_space_hint")}
    `:d} - ${v.some(t=>"default"===t.map_id)?p` -
    ${r("vac.route_default_hint")}
    `:d} - ${z.length?p`
    ${r("vac.route_target_hint",{space:So(i,e.space)})}
    `:d} -
    `}const Do=(t,e,s)=>Math.min(s,Math.max(e,Number.isFinite(t)?t:e));function Co(t){return Number.isFinite(t)?(t%360+360)%360:0}function Po(t){if("string"!=typeof t)return null;const e=t.trim().replace(/^#/,""),s=/^[0-9a-fA-F]{3}$/.test(e)?e.split("").map(t=>`${t}${t}`).join(""):e;return/^[0-9a-fA-F]{6}$/.test(s)?`#${s.toLowerCase()}`:null}function Io(t){const e=Po(t);return e?{r:Number.parseInt(e.slice(1,3),16),g:Number.parseInt(e.slice(3,5),16),b:Number.parseInt(e.slice(5,7),16)}:null}function To(t){const e=Do(t.r,0,255)/255,s=Do(t.g,0,255)/255,i=Do(t.b,0,255)/255,o=Math.max(e,s,i),a=o-Math.min(e,s,i);let n=0;return a>0&&(n=o===e?(s-i)/a%6*60:o===s?60*((i-e)/a+2):60*((e-s)/a+4)),{h:Co(n),s:0===o?0:a/o*100,v:100*o}}function Ro(t){return function(t){const e=t=>Math.round(Do(t,0,255)).toString(16).padStart(2,"0");return`#${e(t.r)}${e(t.g)}${e(t.b)}`}(function(t){const e=Co(t.h),s=Do(t.s,0,100)/100,i=Do(t.v,0,100)/100,o=i*s,a=o*(1-Math.abs(e/60%2-1)),n=i-o;let r;return r=e<60?[o,a,0]:e<120?[a,o,0]:e<180?[0,o,a]:e<240?[0,a,o]:e<300?[a,0,o]:[o,0,a],{r:Math.round(255*(r[0]+n)),g:Math.round(255*(r[1]+n)),b:Math.round(255*(r[2]+n))}}(t))}const zo={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ao extends f{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=zo,this._open=!1,this._pickerRaf=0,this._emitRaf=0,this._forceFallback=!1,this._overlayDispose=null,this._hue=0,this._saturation=0,this._value=0,this._hexDraft="#607d8b",this._hexInvalid=!1,this._lastValidColor="#607d8b",this._activePointerId=null,this._outsidePointerDown=t=>{if(!this._open)return;const e=t.composedPath();this._floating.containsPath(e)||this._closePicker(!1,"outside")},this._keyDown=t=>{this._open&&"Escape"===t.key&&(t.preventDefault(),t.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new Qi(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const t=this._window();t&&(this._pickerRaf=t.requestAnimationFrame(()=>{this._pickerRaf=0,this._positionPicker()||this._closePicker()}))}}connectedCallback(){super.connectedCallback(),this._syncFromColor(this.color,!1),this.ownerDocument.addEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.addEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.addEventListener("resize",this._queuePickerPosition),t?.addEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.addEventListener("resize",this._queuePickerPosition),t?.visualViewport?.addEventListener("scroll",this._queuePickerPosition),this.addEventListener("keydown",this._keyDown,!0)}disconnectedCallback(){this.ownerDocument.removeEventListener("pointerdown",this._outsidePointerDown,!0),this.ownerDocument.removeEventListener("scroll",this._queuePickerPosition,!0);const t=this.ownerDocument.defaultView;t?.removeEventListener("resize",this._queuePickerPosition),t?.removeEventListener("orientationchange",this._queuePickerPosition),t?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),t?.visualViewport?.removeEventListener("scroll",this._queuePickerPosition),this.removeEventListener("keydown",this._keyDown,!0),this._pickerRaf&&cancelAnimationFrame(this._pickerRaf),this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._pickerRaf=0,this._emitRaf=0,this._closePicker(!1,"disconnect"),super.disconnectedCallback()}updated(t){if(t.has("color")){const t=Po(this.color);t&&t!==this._lastValidColor&&this._syncFromColor(t,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(t.has("color")||t.has("opacity")||t.has("showOpacity")||t.has("label")||t.has("opacityLabel")||t.has("pickerLabels"))&&this._queuePickerPosition())}_window(){return this._floating.window()}_supportsPopover(){return this._floating.usesPopover(this._forceFallback)}_dialog(){return this._floating.dialog()}async _toggle(){this.disabled||(this._open?this._closePicker():(this._forceFallback=!1,this._syncFromColor(this.color,!1),this._hexInvalid=!1,this._activePointerId=null,this._open=!0,await this.updateComplete,this._open&&(this._supportsPopover()||this._renderFallback(),this._positionPicker()?this._overlayDispose=this._dialog()?.registerOverlay({owner:this,group:"transient",close:t=>this._closePicker("escape"===t&&this._floating.ownsActiveElement(),t)})||null:this._closePicker())))}_closePicker(t=!1,e="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const s=this._overlayDispose;this._overlayDispose=null,s?.();const i=this.renderRoot.querySelector(".picker");if(i?.hidePopover)try{i.matches(":popover-open")&&i.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),t&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const t=Ao.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),t)}_positionPicker(){if(!this._open)return!1;const t=this._window(),e=this.renderRoot.querySelector(".trigger");let s=this._surface();if(!t||!e?.isConnected||!s?.isConnected)return!1;const i=to(t);if(s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden",this._supportsPopover()&&s.showPopover)try{s.matches(":popover-open")||s.showPopover()}catch{if(this._forceFallback=!0,this._renderFallback(),s=this._surface(),!s?.isConnected)return!1;s.style.maxWidth=`${Math.max(0,i.width-16)}px`,s.style.maxHeight=`${Math.max(0,i.height-16)}px`,s.style.visibility="hidden"}const o=e.getBoundingClientRect(),a=s.getBoundingClientRect();if(!(o.width&&o.height&&a.width&&a.height))return!1;const n=eo(o,a,i);return s.style.left=`${n.left}px`,s.style.top=`${n.top}px`,s.dataset.side=n.side,s.style.visibility="",!0}_labels(){const t=this.pickerLabels||zo;return{title:t.title||zo.title,hue:t.hue||zo.hue,saturation:t.saturation||zo.saturation,value:t.value||zo.value,hex:t.hex||zo.hex,invalidHex:t.invalidHex||zo.invalidHex}}_syncFromColor(t,e){const s=Po(b(t,"#607d8b"))||"#607d8b",i=Io(s);if(!i)return;const o=To(i);(!e||o.s>1e-4)&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._hexDraft=s,this._hexInvalid=!1,this._lastValidColor=s}_cancelQueuedEmit(t=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,t&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const t=this._window();t?this._emitRaf=t.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Ro({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(t){this._hue=Math.min(359,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setSaturation(t){this._saturation=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setValue(t){this._value=Math.min(100,Math.max(0,Number.isFinite(t)?t:0)),this._emitHsv()}_setOpacity(t){this._emit(this._lastValidColor,t)}_shiftRangeKey(t,e,s,i,o){if(!t.shiftKey)return;const a="ArrowRight"===t.key||"ArrowUp"===t.key?10:"ArrowLeft"===t.key||"ArrowDown"===t.key?-10:0;a&&(t.preventDefault(),t.stopPropagation(),o(Math.min(i,Math.max(s,e+a))))}_setSvFromPointer(t,e){const s=t.currentTarget.getBoundingClientRect();s.width&&s.height&&(this._saturation=Math.min(100,Math.max(0,(t.clientX-s.left)/s.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(t.clientY-s.top)/s.height))),e?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(t){if(t.preventDefault(),t.stopPropagation(),null===this._activePointerId){this._activePointerId=t.pointerId;try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}this._setSvFromPointer(t,!0)}}_svPointerMove(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!1))}_svPointerUp(t){if(t.pointerId===this._activePointerId){t.preventDefault(),t.stopPropagation(),this._setSvFromPointer(t,!0),this._activePointerId=null;try{t.currentTarget.releasePointerCapture(t.pointerId)}catch{}}}_svPointerCancel(t){t.pointerId===this._activePointerId&&(t.preventDefault(),t.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(t){t.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(t){const e=t.target.value;this._hexDraft=e,this._hexInvalid=!1;const s=Po(e),i=s?Io(s):null;if(!s||!i)return;const o=To(i);o.s>1e-4&&(this._hue=o.h),this._saturation=o.s,this._value=o.v,this._emit(s,this.opacity,!0)}_commitHex(){const t=Po(this._hexDraft);if(!t)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=t,this._hexInvalid=!1,t!==this._lastValidColor&&this._emit(t,this.opacity)}_hexKeyDown(t){"Enter"===t.key&&(t.preventDefault(),t.stopPropagation(),this._commitHex())}_emit(t,e,s=!1){const i=Po(t)||this._lastValidColor,o=Number.isFinite(e)?e:Number(this.opacity)||0,a=Math.min(1,Math.max(0,o));this.color=i,this.opacity=a,this._lastValidColor=i,s||(this._hexDraft=i),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:i,opacity:a},bubbles:!0,composed:!0}))}_pickerTemplate(t){const e=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),s=Ro({h:this._hue,s:this._saturation,v:this._value}),i=Ro({h:this._hue,s:100,v:100}),o=this._labels(),a=Math.round(this._hue),n=Math.round(this._saturation),r=Math.round(this._value);return p` - `}render(){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),e=b(this.color,"#607d8b"),s=this.showOpacity?`${this.label||"Color"}: ${e}, ${t}%`:`${this.label||"Color"}: ${e}`;return p` - ${this.label?p`${this.label}`:d} - - ${this._open&&!this.disabled&&this._supportsPopover()?this._pickerTemplate(!0):d} - `}}Ao.properties={label:{type:String},opacityLabel:{type:String,attribute:"opacity-label"},color:{type:String},opacity:{type:Number},disabled:{type:Boolean,reflect:!0},showOpacity:{type:Boolean,attribute:"show-opacity"},pickerLabels:{attribute:!1},_open:{state:!0},_hue:{state:!0},_saturation:{state:!0},_value:{state:!0},_hexDraft:{state:!0},_hexInvalid:{state:!0}},Ao.styles=g` - :host { - position: relative; - display: inline-flex; - align-items: center; - gap: 6px; - min-width: 0; - } - .label { - font-size: 12px; - color: var(--secondary-text-color, #9aa4ad); - white-space: nowrap; - } - .trigger { - width: 40px; - height: 40px; - flex: none; - box-sizing: border-box; - padding: 3px; - border: 1px solid var(--divider-color, #666); - border-radius: 7px; - background: - linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, - linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; - cursor: pointer; - } - :host([data-pointer-hover]) .trigger:hover { - border-color: var(--primary-color, #03a9f4); - } - .trigger[aria-expanded='true'] { - border-color: var(--primary-color, #03a9f4); - outline: none; - box-shadow: 0 0 0 1px var(--primary-color, #03a9f4); - } - .trigger:focus-visible { - border-color: var(--primary-color, #03a9f4); - outline: 2px solid var(--primary-color, #03a9f4); - outline-offset: 2px; - } - .swatch { - display: block; - width: 100%; - height: 100%; - border-radius: 3px; - box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.18); - pointer-events: none; - } - .picker { - /* The popover attribute promotes this surface into the browser top layer, outside - hp-dialog's scrolling/clipping body. JS supplies viewport coordinates. */ - position: fixed; - z-index: 2147483647; - inset: auto; - top: 0; - left: 0; - width: min(292px, calc(100vw - 16px)); - max-height: calc(100vh - 16px); - margin: 0; - box-sizing: border-box; - display: grid; - gap: 10px; - padding: 12px; - overflow: auto; - color: var(--primary-text-color, #fff); - background: var(--card-background-color, #202126); - border: 1px solid var(--primary-color, #03a9f4); - border-radius: 10px; - box-shadow: 0 10px 28px rgb(0 0 0 / 0.34); - } - .picker[popover]:not(:popover-open) { - /* Keep the author-level display:grid from exposing the surface for one - frame before showPopover() promotes it into the top layer. */ - display: none; - } - .picker-head, - .row, - .control-head { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; - } - .picker-head { - justify-content: space-between; - } - .caption { - flex: 1; - min-width: 0; - overflow: hidden; - color: var(--secondary-text-color, #9aa4ad); - font-size: 12px; - text-overflow: ellipsis; - white-space: nowrap; - } - .preview { - width: 52px; - height: 30px; - flex: none; - box-sizing: border-box; - padding: 3px; - border: 1px solid var(--divider-color, #666); - border-radius: 7px; - background: - linear-gradient(45deg, #b8b8b8 25%, transparent 25%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 0 0 / 10px 10px, - linear-gradient(45deg, transparent 75%, #b8b8b8 75%) 5px -5px / 10px 10px, - linear-gradient(45deg, #b8b8b8 25%, #eee 25%) 5px 5px / 10px 10px; - } - .preview > span { - display: block; - width: 100%; - height: 100%; - border-radius: 3px; - } - .sv-field { - position: relative; - width: 100%; - height: 132px; - overflow: hidden; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 8px; - background: - linear-gradient(to top, #000, transparent), - linear-gradient(to right, #fff, var(--hp-picker-hue, #f00)); - cursor: crosshair; - touch-action: none; - user-select: none; - } - .sv-thumb { - position: absolute; - left: var(--hp-picker-saturation, 0%); - top: var(--hp-picker-value, 100%); - width: 14px; - height: 14px; - box-sizing: border-box; - border: 2px solid #fff; - border-radius: 50%; - box-shadow: 0 0 0 1px rgb(0 0 0 / .65), 0 1px 3px rgb(0 0 0 / .5); - transform: translate(-50%, -50%); - pointer-events: none; - } - .control { - display: grid; - gap: 2px; - min-width: 0; - } - .control-value { - flex: none; - color: var(--secondary-text-color, #9aa4ad); - font-size: 12px; - } - input[type='range'] { - width: auto; - height: 40px; - min-width: 0; - flex: 1; - margin: -5px 0; - accent-color: var(--primary-color, #03a9f4); - touch-action: none; - } - .hue-range { - accent-color: var(--hp-picker-hue, #f00); - --hp-picker-hue-track: linear-gradient(to right, - #f00 0%, #ff0 16.667%, #0f0 33.333%, #0ff 50%, - #00f 66.667%, #f0f 83.333%, #f00 100%); - --hp-picker-hue-thumb-shadow: - 0 0 0 2px var(--card-background-color, #202126), - 0 0 0 3px var(--primary-text-color, #fff); - } - .hue-range::-webkit-slider-runnable-track { - height: 10px; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 999px; - background: var(--hp-picker-hue-track); - } - .hue-range::-moz-range-track { - height: 10px; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 999px; - background: var(--hp-picker-hue-track); - } - .hue-range::-moz-range-progress { - height: 10px; - border: 0; - border-radius: 999px; - background: transparent; - } - .hue-range::-webkit-slider-thumb { - border-radius: 50%; - box-shadow: var(--hp-picker-hue-thumb-shadow); - } - .hue-range::-moz-range-thumb { - border-radius: 50%; - box-shadow: var(--hp-picker-hue-thumb-shadow); - } - input[type='text'], - input[type='number'] { - min-height: 40px; - box-sizing: border-box; - border: 1px solid var(--divider-color, #666); - border-radius: 6px; - padding: 6px 8px; - color: var(--primary-text-color, #fff); - background: var(--input-fill-color, transparent); - font: inherit; - } - input[type='text'] { - width: 100%; - } - input[type='number'] { - width: 50px; - flex: none; - } - input:focus-visible, - .sv-field:focus-visible { - outline: 2px solid var(--primary-color, #03a9f4); - outline-offset: 1px; - } - input[aria-invalid='true'] { - border-color: var(--error-color, #db4437); - } - .error { - color: var(--error-color, #db4437); - font-size: 11px; - line-height: 1.25; - } - .pct { - color: var(--secondary-text-color, #9aa4ad); - font-size: 12px; - } - :host([disabled]) { - opacity: .5; - pointer-events: none; - } - @media (prefers-reduced-motion: reduce) { - .picker { transition: none !important; } - } - @media (forced-colors: active) { - .hue-range::-webkit-slider-runnable-track { - border-color: ButtonText; - background: Canvas; - } - .hue-range::-moz-range-track { - border-color: ButtonText; - background: Canvas; - } - .hue-range::-webkit-slider-thumb { - box-shadow: none; - } - .hue-range::-moz-range-thumb { - box-shadow: none; - } - } - `,customElements.get("hp-color-opacity")||customElements.define("hp-color-opacity",Ao);const Fo=new WeakMap;function No(t){const e=function(t){const e=t?.connection||t;return!e||"object"!=typeof e&&"function"!=typeof e?null:e}(t);if(!e)return null;let s=Fo.get(e);return s||(s={revision:0,loaded:!1,configEntries:{},manifests:{},listeners:new Set,refs:0},Fo.set(e,s)),s}function Ho(t){t.revision++;for(const e of[...t.listeners])try{e()}catch{}}function Lo(t,e){const s=No(t);if(!s)return()=>{};s.refs++,s.listeners.add(e),async function(t,e){e.loading||"function"!=typeof t?.callWS?e.loading:e.loaded&&e.loadedAt&&Date.now()-e.loadedAt<3e5||(e.loading=(async()=>{const[s,i]=await Promise.allSettled([t.callWS({type:"config_entries/get"}),t.callWS({type:"manifest/list"})]);"fulfilled"===s.status&&Array.isArray(s.value)&&(e.configEntries=Object.fromEntries(s.value.filter(t=>"string"==typeof t?.entry_id).map(t=>[t.entry_id,t]))),"fulfilled"===i.status&&Array.isArray(i.value)&&(e.manifests=Object.fromEntries(i.value.filter(t=>"string"==typeof t?.domain).map(t=>[t.domain,t]))),e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Ho(e)})().catch(()=>{e.loaded=!0,e.loadedAt=Date.now(),e.loading=void 0,Ho(e)}),e.loading)}(t,s),async function(t,e){if(e.unsubscribe||e.subscribing)return e.subscribing;const s=t?.connection?.subscribeMessage;"function"==typeof s&&(e.subscribing=(async()=>{try{e.unsubscribe=await s.call(t.connection,t=>{const s=Array.isArray(t)?t:Array.isArray(t?.entries)?t.entries:t?[t]:[];let i=!1;const o={...e.configEntries};for(const t of s){const e=t?.entry||t?.data?.entry||("string"==typeof t?.entry_id?t:null),s=e?.entry_id;if("string"!=typeof s)continue;const a=t?.type||t?.action||t?.data?.action;"removed"===a||"remove"===a?delete o[s]:o[s]=e,i=!0}i&&(e.configEntries=o,Ho(e))},{type:"config_entries/subscribe"})}catch{}finally{0===e.refs&&(e.unsubscribe?.(),e.unsubscribe=void 0),e.subscribing=void 0}})(),e.subscribing)}(t,s);let i=!1;return()=>{i||(i=!0,s.listeners.delete(e),s.refs=Math.max(0,s.refs-1),s.refs||(s.unsubscribe?.(),s.unsubscribe=void 0))}}function Eo(t){const e=No(t);return e?{revision:e.revision,loaded:e.loaded,configEntries:e.configEntries,manifests:e.manifests}:{revision:0,loaded:!1,configEntries:{},manifests:{}}}function Oo(t,e,s=Eo(t),i){const o=s.manifests[e];return function(t,e){if("function"!=typeof t?.localize)return"";try{const s=`component.${e}.title`,i=t.localize(s);return"string"==typeof i&&i!==s?i:""}catch{return""}}(t,e)||String(o?.name||"")||String(i||"")||e.replace(/_/g," ")}function Bo(t,e,s,i,o){const a=String(e||"").trim();if(!a)return null;const n=o?i.configEntries[o]:null;return{domain:a,label:Oo(t,a,i,n?.title),configEntryId:o,configEntryTitle:n?.title,confidence:s}}function Wo(t){const e={"registry-owner":0,"entity-platform":1,"identifier-fallback":2},s=new Set;return t.filter(t=>!!t).sort((t,s)=>e[t.confidence]-e[s.confidence]||t.label.localeCompare(s.label)||t.domain.localeCompare(s.domain)).filter(t=>{const e=`${t.domain}\n${t.configEntryId||""}`;return!s.has(e)&&(s.add(e),!0)})}function jo(t,e,s,i=Eo(t)){const o=s.entities?.[e]||t?.entities?.[e];if(!o)return null;const a=o.config_entry_id||void 0,n=a?i.configEntries[a]?.domain:"";return Bo(t,o.platform||n,"entity-platform",i,a)}class Go extends f{constructor(){super(...arguments),this.deviceName="",this._metadataConnection=null,this._demoUntil=0,this._demoTimer=0,this._demoGeneration=1,this._demoKind=null,this._reducedMotion=!1,this._onMotionChange=t=>{this._reducedMotion=t.matches,this.requestUpdate()},this._startShortDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&(this._demoGeneration++,this._demoKind="short",this._demoUntil=Date.now()+3300,window.clearTimeout(this._demoTimer),this._demoTimer=window.setTimeout(()=>{this._demoUntil=0,this._demoTimer=0,this._demoKind=null,this.requestUpdate()},3330),this.requestUpdate())},this._toggleContinuousDemo=()=>{const t=this.presentation;t&&"icon_ripple"===t.display&&"none"===t.pulse.kind&&("continuous"===this._demoKind?this._clearDemo():(this._clearDemo(),this._demoGeneration++,this._demoKind="continuous",this._demoUntil=Number.POSITIVE_INFINITY),this.requestUpdate())}}connectedCallback(){super.connectedCallback(),this._motionMedia=window.matchMedia?.("(prefers-reduced-motion: reduce)"),this._reducedMotion=!!this._motionMedia?.matches,this._motionMedia?.addEventListener?.("change",this._onMotionChange),this._ensureMetadata()}disconnectedCallback(){this._metadataRelease?.(),this._metadataRelease=void 0,this._metadataConnection=null,this._clearDemo(),this._motionMedia?.removeEventListener?.("change",this._onMotionChange),this._motionMedia=void 0,super.disconnectedCallback()}willUpdate(t){t.has("hass")&&this._ensureMetadata();const e=this.presentation,s=t.get("presentation");t.has("presentation")&&s?.binding!==e?.binding&&this._clearDemo(),e&&this._demoKind&&("icon_ripple"!==e.display||"none"!==e.pulse.kind)&&this._clearDemo()}_ensureMetadata(){const t=this.hass?.connection||null;t&&t!==this._metadataConnection&&(this._metadataRelease?.(),this._metadataConnection=t,this._metadataRelease=Lo(this.hass,()=>this.requestUpdate()))}_clearDemo(){window.clearTimeout(this._demoTimer),this._demoTimer=0,this._demoUntil=0,this._demoKind=null}get _lang(){return l(this.hass)}_t(t,e){return y(this._lang,t,e)}_providerText(t){return"houseplan"===t.domain?this._t("marker.preview.virtual_provider"):t.label.toLowerCase()===t.domain.toLowerCase()?t.domain:`${t.label} (${t.domain})`}_providers(){return this.presentation&&this.registry?function(t,e,s,i=Eo(t)){if("virtual"===e)return[{domain:"houseplan",label:"House Plan",confidence:"registry-owner"}];const o=e.indexOf(":");if(o<1)return[];const a=e.slice(0,o),n=e.slice(o+1);if("entity"===a){const e=jo(t,n,s,i);return e?[e]:[]}if("device"!==a)return[];const r=s.devices?.[n]||t?.devices?.[n];if(!r)return[];const l=[r.config_entry_id,...Array.isArray(r.config_entries)?r.config_entries:[]].filter(t=>"string"==typeof t&&!!t).map(e=>{const s=i.configEntries[e];return Bo(t,s?.domain,"registry-owner",i,e)});if(l.some(Boolean))return Wo(l);const h=[];for(const[e,o]of Object.entries(s.entities||{}))o?.device_id===n&&null==o?.disabled_by&&h.push(jo(t,e,s,i));return h.some(Boolean)?Wo(h):Wo((Array.isArray(r.identifiers)?r.identifiers:[]).map(e=>{const s=Array.isArray(e)?e[0]:null;return Bo(t,s,"identifier-fallback",i)}))}(this.hass,this.presentation.binding,this.registry,Eo(this.hass)):[]}_providerSummary(t){if(!t.length)return this._t("marker.preview.unknown_provider");const e=t.slice(0,2).map(t=>this._providerText(t)),s=t.length-e.length;return e.join(", ")+(s>0?` · ${this._t("marker.preview.more_sources",{n:s})}`:"")}_sourceSummary(t){const e=t.visualSources;return e.length?1===e.length?`${e[0].name} · ${e[0].eid} · ${this._sourceProvider(e[0].eid)}`:2===e.length?e.map(t=>t.name).join(" · "):this._t("marker.preview.multiple_sources",{n:e.length}):this._t("marker.preview.no_source")}_stateSummary(t){if(!t.visualSources.length)return this._t("marker.preview.no_state");if(1===t.visualSources.length)return t.visualSources[0].stateText||this._t("marker.preview.no_state");const e=[...new Set(t.visualSources.map(t=>t.stateText).filter(Boolean))];return 1===e.length?e[0]:this._t("marker.preview.mixed_states")}_reason(t){return this._t(`marker.preview.reason.${t}`)}_sourceProvider(t){if(!this.registry)return this._t("marker.preview.unknown_provider");const e=jo(this.hass,t,this.registry,Eo(this.hass));return e?this._providerText(e):this._t("marker.preview.unknown_provider")}render(){const t=this.presentation;if(!t)return d;const e=!!this._demoKind&&this._demoUntil>Date.now()&&"icon_ripple"===t.display&&"none"===t.pulse.kind,s=e?(()=>{const e=$(t.pulse,this._demoKind,this._demoGeneration,this._reducedMotion,"short"===this._demoKind?this._demoUntil:null);return{...t,pulse:e,classes:[...t.classes.filter(t=>!t.startsWith("pulse-")),`pulse-${e.kind}`,...e.generation%2==0?["pulse-gen2"]:[]]}})():t,i=this._providers(),o="none"!==t.pulse.kind,a=s.scale*(s.pulse.animated?s.pulse.diameterScale:1);let n=-a/2,r=a/2,l=-a/2,h=a/2;if(s.valueBadge){const t=s.scale*Math.min(4,Math.max(.9,.29*s.valueBadge.text.length+.28)),e=.7*s.scale,i=.6*s.scale;"right"===s.valueBadge.position&&(r=Math.max(r,i+t)),"left"===s.valueBadge.position&&(n=Math.min(n,-i-t)),"top"===s.valueBadge.position&&(l=Math.min(l,-i-e)),"bottom"===s.valueBadge.position&&(h=Math.max(h,i+e))}if(null!=s.lqiText){const t=s.scale*("bottom"===s.valueBadge?.position?1.55:.95);h=Math.max(h,t)}const c=Math.min(1,2.35/Math.max(1,r-n),2.35/Math.max(1,h-l)),u=Math.round(100*c),_=[`left:calc(50% - ${54*((n+r)/2)*c}px)`,`top:calc(50% - ${54*((l+h)/2)*c}px)`,c<1?`transform:scale(${c})`:""].filter(Boolean).join(";"),m=["dev",w(this.hass),...s.classes,"virtual"===s.binding?"virtual":"",s.haDisabled?"ghost ha-disabled":"",null!=s.valueText?"valonly":""].filter(Boolean).join(" "),g=k(s).join(";"),f=e?this._t("continuous"===this._demoKind?"marker.preview.demo_continuous_notice":"marker.preview.demo_short_notice"):this._reason(t.explanation.reason),b=[this.deviceName,this._providerSummary(i),this._stateSummary(t),f,t.valueFullText||"",x(t.valueBadge),null!=t.lqiText&&t.lqiBand?this._t(`marker.lqi_a11y_${t.lqiBand}`,{value:t.lqiText}):""].filter(Boolean).join(". ");return p`
    -
    - ${this._t("marker.preview.title")} - - ${this._t(e?"marker.preview.example":"marker.preview.actual")} - -
    -
    - -
    -
    ${this._t("marker.preview.integration")}${this._providerSummary(i)}
    -
    ${this._t("marker.preview.source")}${this._sourceSummary(t)}
    -
    ${this._t("marker.preview.current_state")}${this._stateSummary(t)}
    -
    ${this._t("marker.preview.result")}${f}
    - ${t.fallbackReason?p`

    ${this._reason(t.fallbackReason)}

    `:d} - ${t.explanation.notices.map(t=>p`

    ${this._reason(t)}

    `)} - ${c<1?p`

    ${this._t("marker.preview.scaled",{n:u})}

    `:d} - ${e&&this._reducedMotion?p`

    ${this._t("marker.preview.reduced_motion")}

    `:d} -
    -
    - ${t.visualSources.length||t.criticalSources.length?p`
    - ${this._t("marker.preview.details")} -
    - ${[...t.visualSources,...t.criticalSources].map(t=>p` -
    - ${t.name}${t.eid} - ${t.stateText||t.state} - ${this._sourceProvider(t.eid)} -
    `)} -
    -
    `:d} - ${"icon_ripple"===t.display?p`
    - - -
    `:d} -
    `}}Go.properties={hass:{attribute:!1},presentation:{attribute:!1},registry:{attribute:!1},deviceName:{attribute:!1}},Go.styles=[v,g` - :host { display: block; min-width: 0; } - .devicepreview { - margin: 4px 0 14px; - padding: 14px; - border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.28)); - border-radius: 14px; - background: color-mix(in srgb, var(--card-background-color, #20232b) 92%, var(--primary-color, #03a9f4)); - color: var(--primary-text-color, #fff); - overflow: hidden; - } - .previewhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; } - .previewbadge { - padding: 3px 9px; - border-radius: 999px; - color: var(--secondary-text-color, #9aa0aa); - background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 12%, transparent); - font-size: 12px; - font-weight: 700; - } - .previewbadge.example { color: var(--warning-color, #ffb300); } - .previewgrid { display: grid; grid-template-columns: minmax(170px, 0.8fr) minmax(240px, 1.2fr); gap: 16px; align-items: stretch; } - .previewstage { - position: relative; - min-height: 168px; - border-radius: 12px; - background: var(--secondary-background-color, rgba(0, 0, 0, 0.16)); - overflow: hidden; - pointer-events: none; - container-type: inline-size; - --icon-size: 54px; - --device-base-size: 48.6px; - } - .previewfit { position: absolute; left: 50%; top: 50%; transform-origin: center; } - .previewstage .dev { left: 0; top: 0; cursor: default; } - :host([data-pointer-hover]) .previewstage .dev:hover { z-index: 2; } - .previewfacts { min-width: 0; display: grid; align-content: start; gap: 8px; } - .previewfacts > div { display: grid; grid-template-columns: minmax(115px, 0.8fr) minmax(0, 1.3fr); gap: 8px; } - .previewfacts span { color: var(--secondary-text-color, #9aa0aa); } - .previewfacts b { min-width: 0; overflow-wrap: anywhere; } - .previewnotice { margin: 2px 0 0; color: var(--secondary-text-color, #9aa0aa); font-size: 13px; } - .previewdetails { margin-top: 12px; } - .previewdetails summary { cursor: pointer; color: var(--secondary-text-color, #9aa0aa); } - .previewtable { display: grid; gap: 8px; margin-top: 9px; } - .previewsource { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 3px 10px; font-size: 13px; } - .previewsource > * { min-width: 0; overflow-wrap: anywhere; } - .previewsource code { color: var(--secondary-text-color, #9aa0aa); } - .previewdemo { - min-height: 40px; - display: inline-flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border: 1px solid var(--divider-color, rgba(127, 127, 127, 0.35)); - border-radius: 10px; - background: transparent; - color: inherit; - cursor: pointer; - } - .previewdemos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; } - .previewdemo:focus-visible { outline: 2px solid var(--primary-color, #03a9f4); outline-offset: 2px; } - .previewdemo:disabled { opacity: 0.5; cursor: not-allowed; } - @media (max-width: 640px) { - .previewgrid { grid-template-columns: minmax(0, 1fr); } - .previewstage { min-height: 142px; } - .previewfacts > div { grid-template-columns: minmax(0, 1fr); gap: 2px; } - } - `],customElements.get("hp-device-preview")||customElements.define("hp-device-preview",Go);const qo={revision:0,topologies:[],states:{}};class Uo extends f{constructor(){super(...arguments),this.value={enabled:!1,z2mBaseTopics:[]},this.savedEnabled=!1,this.devices=[],this._runtime=null,this._snapshot=qo,this._topicText="zigbee2mqtt",this._invalidTopic=!1}disconnectedCallback(){this._release?.(),this._release=void 0,super.disconnectedCallback()}updated(t){t.has("hass")&&(this._release?.(),this._release=void 0,this._runtime=null,this._snapshot=qo),this.savedEnabled&&this._admin&&this._ensureRuntime()}willUpdate(t){t.has("value")&&(this._topicText=(this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]).join("\n"),this._invalidTopic=!1)}get _admin(){return!0===this.hass?.user?.is_admin}_t(t,e){return po(l(this.hass),t,e)}async _ensureRuntime(){return this._runtime||(this._runtime=await import("./zigbee-topology-runtime-BFjOXm55.js"),this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this._release=this._runtime.subscribeZigbeeTopology(this.hass,()=>{this._snapshot=this._runtime.zigbeeTopologyRuntimeSnapshot(this.hass),this.requestUpdate()}),this.requestUpdate()),this._runtime}_emit(t){this._admin&&this.dispatchEvent(new CustomEvent("hp-topology-settings-change",{detail:t,bubbles:!0,composed:!0}))}_topics(){return this.value.z2mBaseTopics.length?this.value.z2mBaseTopics:["zigbee2mqtt"]}_editTopics(t){this._topicText=t;const e=t.split(/[\r\n,]+/).map(t=>t.trim()).filter(Boolean),s=e.map(M).filter(Boolean);this._invalidTopic=s.length!==e.length,this._invalidTopic?this.requestUpdate():this._emit({...this.value,z2mBaseTopics:[...new Set(s)].slice(0,8)})}_status(t){const e=this._snapshot.states[t];if(!e)return this._t("status_idle");if("loading"===e.phase)return this._t("status_loading");if("error"===e.phase)return this._t(`error_${e.error||"provider"}`);if("ready"!==e.phase||!e.obtainedAt)return this._t("status_idle");const s=new Date(e.obtainedAt).toLocaleTimeString(l(this.hass),{hour:"2-digit",minute:"2-digit"}),i=this._snapshot.topologies.find(e=>"zha"===t?"zha"===e.provider:`z2m:${e.instanceId}`===t),o=!!i&&!!this.registry&&uo(i,this.devices,this.registry).warnings.length>0;return e.partial||o?this._t("status_partial",{time:s}):i&&!i.links.length?this._t("status_no_links",{time:s}):this._t(Date.now()-e.obtainedAt>_o?"status_stale":"status_ready",{time:s})}_busy(t){return"loading"===this._snapshot.states[t]?.phase}async _readZha(){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).readZhaTopology(this.hass)}async _refreshZ2m(t){this.savedEnabled&&this._admin&&(await this._ensureRuntime()).refreshZ2mTopology(this.hass,t)}render(){const t=this._admin,e=this.value.enabled,s=t&&this.savedEnabled,i=this._topics();return p` -
    ${this._t("title")}
    - -
    ${this._t("hint")}
    - ${t?d:p`
    ${this._t("admin_only")}
    `} - ${e?p`
    - ${this.savedEnabled?d:p`
    ${this._t("save_first")}
    `} -
    -
    ${this._t("zha")}
    -
    ${this._t("zha_hint")}
    -
    - - ${this._status("zha")} -
    -
    -
    -
    ${this._t("z2m")}
    - - - ${this._invalidTopic?p`
    ${this._t("error_invalid_topic")}
    `:d} -
    ${this._t("z2m_warning")}
    - ${i.map(t=>p`
    - - ${this._status(`z2m:${t}`)} -
    `)} -
    -
    `:d} - `}}Uo.properties={hass:{attribute:!1},value:{attribute:!1},savedEnabled:{type:Boolean,attribute:"saved-enabled"},devices:{attribute:!1},registry:{attribute:!1}},Uo.styles=g` - :host { display: block; color: inherit; font: inherit; } - .section { margin-top: 18px; font-weight: 700; } - .toggle { display: flex; align-items: center; gap: 10px; margin-top: 10px; } - .hint, .status { color: var(--secondary-text-color, #9aa0aa); font-size: 12px; line-height: 1.45; } - .hint { margin-top: 6px; } - .providers { display: grid; gap: 14px; margin-top: 12px; padding-left: 34px; } - .provider { display: grid; gap: 7px; } - .provider-title { font-weight: 650; } - .actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } - textarea { - box-sizing: border-box; width: 100%; min-height: 58px; resize: vertical; - border: 1px solid var(--divider-color, #666); border-radius: 8px; padding: 8px; - color: var(--primary-text-color, inherit); background: var(--card-background-color, #202126); - font: inherit; - } - button { - display: inline-flex; align-items: center; gap: 6px; min-height: 36px; - border: 1px solid var(--divider-color, #666); border-radius: 9px; padding: 7px 11px; - color: inherit; background: transparent; cursor: pointer; font: inherit; font-weight: 600; - } - button:disabled, textarea:disabled { opacity: .5; cursor: default; } - .warning { color: var(--warning-color, #d89300); font-size: 12px; line-height: 1.45; } - `,customElements.get("hp-zigbee-topology-settings")||customElements.define("hp-zigbee-topology-settings",Uo);const Ko=(t,e)=>[t[0]-e[0],t[1]-e[1]],Vo=(t,e)=>[t[0]+e[0],t[1]+e[1]],Jo=(t,e)=>t[0]*e[0]+t[1]*e[1],Yo=t=>Math.hypot(t[0],t[1]);function Zo(t){let e=0;for(let s=0;st[1]!=l>t[1]&&t[0]<(r-a)*(t[1]-n)/(l-n)+a&&(s=!s)}return s}([r[0]+n[0]*l,r[1]+n[1]*l],t)&&(n=[-n[0],-n[1]]),n}function ta(t){const e=t.length;if(e<3)return!1;for(let s=0;s(t[0]-o[0])*r[1]-(t[1]-o[1])*r[0],h=t=>(t[0]-o[0])*r[0]+(t[1]-o[1])*r[1],c=s,d=n-s;for(const e of t){const t=Math.abs(l(e));if(t<=s)continue;const o=h(e);o<=c||o>=d||tMath.abs(n+(t-p)/g*(r-n));i=Math.min(i,f(_),f(m))}}return i}function sa(t,e,s){if(I(t,e,s))return!0;if(P(t,e,s)||P(e,t,s))return!1;let i=0;try{const s=T([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of s)t?.[0]&&(i+=R(t[0]))}catch{return!1}return i>Math.max(1e-7,s*s)}function ia(t,e,s){for(const i of e)for(let e=0;es)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-s&&h+t.length/2<=r+s)return!0}return!1}function oa(t,e,s){const i=Math.abs(e[0]-t[0]),o=Math.abs(e[1]-t[1]);return i<=s&&o>s?"v":o<=s&&i>s?"h":null}function aa(t,e,s){return Math.abs(t[0]-e[0])<=s&&Math.abs(t[1]-e[1])<=s}function na(t,e,s,i,o){return aa(t,s,o)&&aa(e,i,o)||aa(t,i,o)&&aa(e,s,o)}function ra(t,e,s,i,o){const a=oa(t,e,o);return a&&oa(s,i,o)===a?"h"===a?Math.abs(t[1]-s[1])>o||Math.abs(t[1]-i[1])>o?0:Math.max(0,Math.min(Math.max(t[0],e[0]),Math.max(s[0],i[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s[0],i[0]))):Math.abs(t[0]-s[0])>o||Math.abs(t[0]-i[0])>o?0:Math.max(0,Math.min(Math.max(t[1],e[1]),Math.max(s[1],i[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s[1],i[1]))):0}function la(t,e,s,i){const o=t.length,a=oa(t[(e-1+o)%o],t[e],i),n=oa(t[(e+1)%o],t[(e+2)%o],i),r="h"===s?"v":"h";return a===r&&n===r}function ha(t){let e=1/0,s=1/0,i=-1/0,o=-1/0;for(const a of t)e=Math.min(e,a[0]),s=Math.min(s,a[1]),i=Math.max(i,a[0]),o=Math.max(o,a[1]);return[e,s,i,o]}function ca(t,e,s){const i=ha(t),o=ha(e);return i[2]o}const pa=(t,e)=>"h"===e?t[0]:t[1],ua=(t,e)=>"h"===e?t[1]:t[0];function _a(t,e,s,i,o){const a=t.find(t=>t.id===e);if(!a)return null;const n=a.poly[s],r=a.poly[(s+1)%a.poly.length],l=oa(n,r,o);if(!l)return null;const h=0===i?r:n,c=0===i?n:r,d=pa(h,l),p=pa(c,l),u=ua(h,l),_=function(t,e,s,i){const o=[];for(const a of t)for(let t=0;ti||Math.abs(ua(r,e)-s)>i)continue;const l=pa(n,e),h=pa(r,e),c=Math.min(l,h),d=Math.max(l,h);d-c>i&&o.push({roomId:a.id,edge:t,lo:c,hi:d})}return o.sort((t,e)=>t.lo-e.lo||t.hi-e.hi||t.roomId.localeCompare(e.roomId)||t.edge-e.edge)}(t,l,u,o),m=function(t,e,s,i){const o=[e,s];for(const a of t){const t=Math.max(e,a.lo),n=Math.min(s,a.hi);n-t>i&&o.push(t,n)}const a=[...new Set(o)].sort((t,e)=>t-e),n=[];for(let e=0;e+1r>t.lo-i&&rt.roomId))].sort();n.push({lo:s,hi:o,owners:l})}return n}(_,Math.min(d,p),Math.max(d,p),o);return!m.length||m.some(t=>t.owners.length<1||t.owners.length>2)?null:{roomId:e,edge:s,movedEndpoint:i,axis:l,line:u,fixed:d,moving:p,intervals:_,baseline:m}}function ma(t,e,s){const i=[];for(const o of t.intervals){const a=e.polys[o.roomId];if(!a){i.push(o);continue}if(o.edge<0||o.edge>=a.length)continue;const n=a[o.edge],r=a[(o.edge+1)%a.length];if(oa(n,r,s)!==t.axis||Math.abs(ua(n,t.axis)-t.line)>s||Math.abs(ua(r,t.axis)-t.line)>s)continue;const l=pa(n,t.axis),h=pa(r,t.axis),c=Math.min(l,h),d=Math.max(l,h);d-c>s&&i.push({...o,lo:c,hi:d})}return i}function ga(t,e,s){return new Set(t.filter(t=>e>t.lo-s&&et.roomId)).size}function fa(t,e,s){for(const i of e.sideOwnership){const e=t.polys[i.roomId];if(!e)return!1;const o=e[i.edge],a=e[(i.edge+1)%e.length];if(oa(o,a,s)!==i.axis)return!1;const n=pa(0===i.movedEndpoint?o:a,i.axis),r=ma(i,t,s),l=(i.moving>=i.fixed?i.baseline[i.baseline.length-1]:i.baseline[0]).owners.length;if(l<1||l>2)return!1;const h=Math.min(i.fixed,i.moving,n),c=Math.max(i.fixed,i.moving,n),d=[h,c,i.fixed,i.moving,n];for(const t of i.intervals)d.push(t.lo,t.hi);for(const t of r)d.push(t.lo,t.hi);const p=[...new Set(d.filter(t=>t>=h&&t<=c))].sort((t,e)=>t-e),u=Math.min(i.fixed,i.moving),_=Math.max(i.fixed,i.moving),m=Math.min(i.fixed,n),g=Math.max(i.fixed,n);for(let t=0;t+1u-s&&a<_+s,h=a>m-s&&a2||d>2)return!1;if(n&&h){if(c!==d)return!1}else if(!n&&h){if(d!==l||c>0&&c!==d)return!1}else if(2===c?2!==d:d>1)return!1}}return!0}function ba(t,e,s,i){const o=[s.n[0]*i,s.n[1]*i],a={polys:{},openings:{},movedSpans:{}};for(const e of s.roomIds){const i=t.find(t=>t.id===e),n=s.edgeByRoom[e];if(!i||null==n)continue;const r=(n+1)%i.poly.length;a.polys[e]=i.poly.map((t,e)=>e===n||e===r?Vo(t,o):[...t]),a.movedSpans[e]=[[Vo(i.poly[n],o),Vo(i.poly[r],o)]]}for(const t of e)s.movingOpeningIds.includes(t.id)&&(a.openings[t.id]=[t.x+o[0],t.y+o[1]]);return a}function va(t,e,s,i,o,a,n,r){if(!function(t,e,s,i){if(Xo([t.x,t.y],e,s)>2*i)return!1;const o=Yo(Ko(s,e));if(o<=i)return!1;const a=[(s[0]-e[0])/o,(s[1]-e[1])/o],n=(t.x-e[0])*a[0]+(t.y-e[1])*a[1];return n>=-i&&n<=o+i}(t,e,s,r))return!0;if(Xo([t.x,t.y],i,o)>2*r)return!1;const l=0===a?i:o;return Yo(Ko([t.x,t.y],l))>=t.length/2+n-r}function ya(t,e,s,i){const o=i.movingHalf||0;return"circle"===t.kind?Xo(t.center,e,s)2)return!1;const r=ba(t,e,s,i);if(Object.keys(r.polys).length!==s.roomIds.length)return!1;if(!fa(r,s,a))return!1;const l=new Set(s.roomIds),h=t=>r.polys[t.id]||t.poly;for(const i of s.roomIds){const l=t.find(t=>t.id===i),h=r.polys[i],c=s.edgeByRoom[i];if(!l||!h||h.length!==s.topology[i]||h.length!==l.poly.length||!ta(h))return!1;const d=l.poly.length,p=(c-1+d)%d,u=[p,c,(c+1)%d];for(const t of u)if(C(h[t],h[(t+1)%d]))return!1;const _=Zo(l.poly),m=Zo(h);if(Math.abs(m)t.id===e),i=r.polys[e];for(const o of t){if(o.id===e||l.has(o.id))continue;const t=h(o);if(!ca(s.poly,o.poly,a)||!ca(i,t,a))if(P(s.poly,o.poly,a)){if(!P(i,t,a))return!1}else if(P(o.poly,s.poly,a)){if(!P(t,i,a))return!1}else if(P(i,t,a)||P(t,i,a)||sa(i,t,a))return!1}}t.find(t=>t.id===s.roomId);const c=r.polys[s.roomId],d=s.edgeByRoom[s.roomId],p=c[d],u=c[(d+1)%c.length];if(Math.abs(i)>a)for(const t of o.obstacles||[])if(ya(t,p,u,o))return!1;for(const t of e){if(!s.movingOpeningIds.includes(t.id))continue;const e=r.openings[t.id];if(!e||t.hosted)return!1;if(!ia({...t,x:e[0],y:e[1]},[c],2*a))return!1}return!0}const wa=new WeakMap,ka=t=>"number"==typeof t&&Number.isFinite(t),xa=(t,e)=>{const s=new Map;for(const i of t||[])!ka(i?.cm)||!e&&i.cm<=0||s.set(i.cm,(s.get(i.cm)||0)+1);return s},Sa=t=>[String(t),"см"].join(" ");function Ma(t,e,s={}){if(s.allowClear)return[];const i=!0===s.exactMultiplicity,o=xa(t,i),a=xa(e,i),n=[];for(const[t,e]of o){const s=a.get(t)||0;0===s?n.push({invariant:"wall_records",kind:"lost",owner:Sa(t),reference:`было ${e}`,detail:"записи этой толщины исчезли целиком"}):i&&s!==e&&n.push({invariant:"wall_records",kind:"count",owner:Sa(t),reference:`было ${e}, стало ${s}`,detail:"изменилось число записей этой толщины"})}if(i)for(const[t,e]of a)o.has(t)||n.push({invariant:"wall_records",kind:"count",owner:Sa(t),reference:`было 0, стало ${e}`,detail:"появились записи новой толщины"});return n}class Da{constructor(){this._selectedRoomId=null,this._session=null,this._eligibility=null}get dragging(){return null!==this._session}ownsPointer(t){return this._session?.pointerId===t}get selectedRoomId(){return this._selectedRoomId}get snapshotIdentity(){return this._session?.snapshotIdentity||null}get activePointerId(){return this._session?.pointerId??null}get moved(){return!0===this._session?.moved}get delta(){return this._session?.delta||0}get rooms(){return this._session?.rooms||null}get openings(){return this._session?.openings||null}get plan(){return this._session?.plan||null}get preview(){return this._session?.accepted?.preview||null}get liveLabels(){return this._session?.accepted?.labels||null}selectRoom(t){this._selectedRoomId=t}restoreSelection(t){this._selectedRoomId=t}escapeIdle(){return this._selectedRoomId?(this._selectedRoomId=null,"selection-cleared"):"exit-tool"}resolve(t,e,s){this._eligibility&&this._eligibility.context===t||(this._eligibility={context:t,values:new Map});const i=this._eligibility.values.get(e);if(i)return i;const o=s();return this._eligibility.values.set(e,o),o}begin(t){return!this._session&&(this._session={pointerId:t.pointerId,start:[...t.start],roomId:t.roomId,plan:t.plan,options:t.options,rooms:t.rooms,openings:t.openings,snapshotIdentity:t.snapshotIdentity,before:t.before,moved:!1,delta:0,changedRoomIds:[...t.plan.roomIds],rejectionNotified:!1,wallUnionBefore:t.wallUnionBefore,accepted:null},!0)}move(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=(i=e.start,o=t.point,a=e.plan.n,[i?.[0],i?.[1],o?.[0],o?.[1],a?.[0],a?.[1]].every(Number.isFinite)?(o[0]-i[0])*a[0]+(o[1]-i[1])*a[1]:0);var i,o,a;const n=t.snap([e.plan.a[0]+e.plan.n[0]*s,e.plan.a[1]+e.plan.n[1]*s]),r=(n[0]-e.plan.a[0])*e.plan.n[0]+(n[1]-e.plan.a[1])*e.plan.n[1],l=function(t,e,s,i,o,a){if(!Number.isFinite(i)||Math.abs(i)<1e-9)return 0;const n=Math.sign(i),r=Math.abs(i),l=Math.max(Math.abs(o),1e-6);let h=wa.get(s);h&&h.opts===a||(h={opts:a,values:new Map},wa.set(s,h));const c=h.values;let d=0;for(let i=Math.min(l,r),o=0;o<4096&&i<=r+1e-9;o++,i=Math.min(r,i+l)){const o=n*i,h=`${o.toFixed(9)}|${l.toFixed(9)}`;let p=c.get(h);if(void 0===p&&(p=$a(t,e,s,o,a),c.size<4096&&c.set(h,p)),!p)break;if(d=o,Math.abs(i-r)<=1e-9)break}return d}(e.rooms,e.openings,e.plan,r,t.step,e.options);if(e.moved&&l===e.delta)return{kind:"no-op"};const h=ba(e.rooms,e.openings,e.plan,l);let c;try{if(c=t.project(e.snapshotIdentity,h.polys,h.openings,e.changedRoomIds,e.rooms),!c.ok)return this._projectionRejected(e);if(Ma(c.value.beforeWalls,c.value.afterWalls,{exactMultiplicity:!0}).length)return this._projectionRejected(e)}catch{return this._projectionRejected(e)}const d=e.accepted,p={preview:c.value.preview,labels:null,artifact:c.value.artifact,beforeWalls:c.value.beforeWalls,afterWalls:c.value.afterWalls};e.accepted=p;try{t.publish(p.preview,p.artifact),p.labels=t.measure(h,e.plan)}catch{e.accepted=d;try{t.publish(d?.preview||null,d?.artifact||null)}catch{}return this._projectionRejected(e)}return e.moved=!0,e.delta=l,{kind:"accepted",preview:c.value.preview,labels:p.labels,artifact:c.value.artifact}}finish(t){const e=this._session;if(!e||e.pointerId!==t.pointerId)return{kind:"no-op"};const s=e.accepted;let i;if(e.moved&&Math.abs(e.delta)>1e-9&&s)if(t.currentSnapshotIdentity!==e.snapshotIdentity)i={kind:"rejected",reason:"stale-snapshot"};else if($a(e.rooms,e.openings,e.plan,e.delta,e.options))if(Ma(s.beforeWalls,s.afterWalls,{exactMultiplicity:!0}).length)i={kind:"rejected",reason:"wall-records"};else{let o=!1;try{o=t.validatePreview(s.preview)}catch{o=!1}i=o?{kind:"commit",preview:s.preview,before:e.before}:{kind:"rejected",reason:"invalid-candidate"}}else i={kind:"rejected",reason:"invalid-topology"};else i={kind:"no-op"};return this._session=null,"commit"===i.kind&&(this._eligibility=null),i}cancel(t,e){const s=this._session;return!s||void 0!==e&&s.pointerId!==e?{kind:"no-op"}:(this._session=null,this._eligibility=null,{kind:"cancelled",restoreWallUnion:t===s.snapshotIdentity?s.wallUnionBefore:null})}reset(){this._selectedRoomId=null,this._session=null,this._eligibility=null}_projectionRejected(t){const e=!t.rejectionNotified;return t.rejectionNotified=!0,{kind:"rejected",notify:e}}}const Ca=(t,e)=>[(t[0]-e.x)/Math.max(1e-9,e.w)*e.stageWidth,(t[1]-e.y)/Math.max(1e-9,e.h)*e.stageHeight];function Pa(t){const{poly:e,edge:s,text:i,view:o}=t,a=e[s],n=e[(s+1)%e.length],r=[(a[0]+n[0])/2,(a[1]+n[1])/2],l=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return(t=>{let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(e,s),h=Math.hypot(n[0]-a[0],n[1]-a[1])||1,c=[(n[0]-a[0])/h,(n[1]-a[1])/h],d=Ca(r,o),p=Ca(t.gearCenter,o),u=Math.max(34,7.2*i.length+12),_=Math.abs(l[0])>=Math.abs(l[1])?Math.max(28,u/2+4):Math.max(28,13),m=d[0]+l[0]*_,g=d[1]+l[1]*_,f=e=>{return s=m+c[0]*e,i=g+c[1]*e,o=u,a=18,n=p[0],r=p[1],l=t.gearWidthPx,h=t.gearHeightPx,2*Math.abs(s-n)=Math.abs(c[1])?c[0]:c[1])>0?-1:1,e=Math.max(o.stageWidth,o.stageHeight,64);for(let s=4;s<=e;s+=4){const e=s*t;if(!f(e)){b=e;break}const i=-e;if(!f(i)){b=i;break}}}const v=l[0]*_+c[0]*b,y=l[1]*_+c[1]*b,$=Math.hypot(v,y)||1,w=y/$*12,k=[r[0]+v/$*12*o.w/Math.max(1,o.stageWidth),r[1]+w*o.h/Math.max(1,o.stageHeight)];return{anchor:r,offsetXPx:v,offsetYPx:y,tangentOffsetPx:b,side:Math.abs(l[0])>=Math.abs(l[1])?l[0]<0?"left":"right":l[1]<0?"above":"below",leader:{a:r,b:k}}}const Ia=t=>{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Ta(t,e,s,i,o,a=0,n=[t,e],r){const l=1e-7,h=(t,e,s,i,o)=>{const a=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*i)/o));return{t:a,x:e[0]+a*s,y:e[1]+a*i}},c=t=>{if(!t||!Number.isFinite(t[0])||!Number.isFinite(t[1]))return null;const e=Math.hypot(t[0],t[1]);return e>1e-9?[t[0]/e,t[1]/e]:null},d=c(r);let p=null;for(const r of i){const i=Number(r?.a?.[0]),u=Number(r?.a?.[1]),_=Number(r?.b?.[0]),m=Number(r?.b?.[1]);if(![i,u,_,m].every(Number.isFinite))continue;const g=_-i,f=m-u,b=g*g+f*f;if(!(b>1e-18))continue;const v=Math.sqrt(b),y=h(n,[i,u],g,f,b),$=Math.hypot(n[0]-y.x,n[1]-y.y);if(!($=2?r.axisA:r.a,e=r.axisB?.length>=2?r.axisB:r.b,s=Number(e[0])-Number(t[0]),i=Number(e[1])-Number(t[1]),o=s*s+i*i;if(o>1e-18&&[t[0],t[1],e[0],e[1]].every(Number.isFinite)){const e=h(n,t,s,i,o),a=(n[0]-e.x)*w[0]+(n[1]-e.y)*w[1];if(a>l)k=2;else if(a<-1e-7){if(r.roomSide)continue;k=-2}else d&&(k=w[0]*d[0]+w[1]*d[1])}}else{let t=n[0]-y.x,e=n[1]-y.y;const s=Math.hypot(t,e);if(s>l)t/=s,e/=s,k=2;else if(d)[t,e]=d,k=1;else{const s=i<_||i===_&&u<=m;t=-(s?f:-f)/v,e=(s?g:-g)/v}w=[t,e]}const x=h([t,e],[i,u],g,f,b);let S=x.t,M=x.x,D=x.y;if(Number.isFinite(a)&&a>0){const t=Math.round(S*v/a)*a;S=Math.max(0,Math.min(1,t/v)),M=i+S*g,D=u+S*f}const C=Ia(180*Math.atan2(-w[0],w[1])/Math.PI),P={cx:M+w[0]*(s/2),cy:D+w[1]*(s/2),angle:C,dist:$,sideScore:k,stableId:"string"==typeof r.stableId?r.stableId:""};(!p||P.distp.sideScore+l||Math.abs(P.sideScore-p.sideScore)<=l&&P.stableId.localeCompare(p.stableId)<0))&&(p=P)}return p&&{cx:p.cx,cy:p.cy,angle:p.angle,dist:p.dist}}const Ra=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),za=(t,e)=>{const s=t=>`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=s(t),o=s(e);return i<=o?`${i}|${o}`:`${o}|${i}`};function Aa(t,e,s,i,o,a,n=1){const r=[],l=new Map;for(const h of t||[]){const c="string"==typeof h?.id?h.id:"";if(!c)continue;const d=H(t,c,e,s,i,o,a,n);if(d)for(let t=0;t1e-9))continue;const a=L(d.poly,t),n=Number(d.offsets[t]),h=Number.isFinite(n)&&n>0?n:0,p=[e[0],e[1]],u=[s[0],s[1]],_=za(p,u),m=l.get(_)||new Set;m.add(c),l.set(_,m),r.push({a:[p[0]+a[0]*h,p[1]+a[1]*h],b:[u[0]+a[0]*h,u[1]+a[1]*h],axisA:p,axisB:u,normal:a,owner:"room",stableId:`room:${c}:${_}`,roomId:c,atomId:_,half:h})}}const h=[];for(const t of r){const{atomId:e,half:s,...i}=t;if(!(1===l.get(e)?.size&&s>1e-9)){h.push(i);continue}const o=i.normal;h.push({...i,roomSide:"inside",stableId:`${i.stableId}:inside`}),h.push({...i,a:[i.axisA[0]-o[0]*s,i.axisA[1]-o[1]*s],b:[i.axisB[0]-o[0]*s,i.axisB[1]-o[1]*s],normal:[-o[0],-o[1]],roomSide:"outside",stableId:`${i.stableId}:outside`})}return h}function Fa(t){const e=[],s=new Set;for(const i of t||[])if(Array.isArray(i)&&!(i.length<2))for(let t=0;t1e-9))continue;const n=[o[0],o[1]],r=[a[0],a[1]],l=`physical:${za(n,r)}`;s.has(l)||(s.add(l),e.push({a:n,b:r,axisA:[...n],axisB:[...r],normal:null,owner:"physical",stableId:l}))}return e}const Na=new WeakMap;const Ha=1e-9,La=1e7,Ea=t=>{const e=Math.sign(t)*Math.floor(Math.abs(t)*La+.5)/La;return Object.is(e,-0)?0:e},Oa=t=>`${Ea(t[0])},${Ea(t[1])}`,Ba=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),Wa=(t,e)=>Math.atan2(e[1]-t[1],e[0]-t[0]),ja=t=>Array.isArray(t)&&t.length>=2&&t.every(t=>Number.isFinite(t)),Ga=t=>(t||[]).filter(t=>ja(t?.a)&&ja(t?.b)&&Ba(t.a,t.b)>Ha);function qa(t,{minAngleDeg:e=15,maxValence:s=6}={}){const i=new Map;for(const e of Ga(t))for(const[t,s]of[[e.a,e.b],[e.b,e.a]]){const e=i.get(Oa(t))||[];e.push(Wa(t,s)),i.set(Oa(t),e)}const o=[];for(const[t,a]of i){if(a.length>s&&o.push({rule:"valence",subject:t,actual:a.length,limit:s}),a.length<2)continue;const i=[...a].sort((t,e)=>t-e);let n=1/0;for(let t=0;tHa&&s(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI%180+180)%180,Ka=(t,e,s=1)=>{const i=Math.abs(Ua(t)-Ua(e));return Math.min(i,180-i)<=s},Va=t=>{const e=new Map;for(const s of t)for(const t of[s.a,s.b]){const i=e.get(Oa(t));i?i.push(s):e.set(Oa(t),[s])}return e};function Ja(t,e,s){const i=Ga(e),o=s??Va(i),a=new Set([t]);let n=Ba(t.a,t.b);const r=[t.a,t.b];for(;r.length;){const e=r.pop();for(const s of o.get(Oa(e))||[])a.has(s)||Ka(s,t)&&Number(s.cm||0)===Number(t.cm||0)&&(a.add(s),n+=Ba(s.a,s.b),r.push(s.a,s.b))}return n}function Ya(t,e,s,{minLengthCm:i=20}={}){const o=[],a=Ga(t),n=Va(a);for(const t of a){const r=Ja(t,a,n)/s*(e||1),l=Math.max(i,Number(t.cm)>0?Number(t.cm):0);r{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a<=Ha?0:Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))};function Xa(t,e,s,{minDistanceCm:i=5}={}){const o=Ga(t),a=new Map;for(const t of o)a.set(Oa(t.a),t.a),a.set(Oa(t.b),t.b);const n=((t,e,s)=>t/(e||1)*s)(i,e,s),r=n>Ha?n:1,l=(t,e)=>`${Math.floor(t/r)},${Math.floor(e/r)}`,h=new Map;for(const[t,e]of a){const s=l(e[0],e[1]),i=h.get(s);i?i.push([t,e]):h.set(s,[[t,e]])}const c=new Map;for(const t of o){const e=Math.min(t.a[0],t.b[0])-n,s=Math.max(t.a[0],t.b[0])+n,i=Math.min(t.a[1],t.b[1])-n,o=Math.max(t.a[1],t.b[1])+n;for(let a=Math.floor(e/r);a<=Math.floor(s/r);a++)for(let e=Math.floor(i/r);e<=Math.floor(o/r);e++){const s=`${a},${e}`,i=c.get(s);i?i.push(t):c.set(s,[t])}}const d=[];for(const[t,o]of a){const a=Math.floor(o[0]/r),l=Math.floor(o[1]/r);for(let r=-1;r<=1;r++)for(let c=-1;c<=1;c++)for(const[p,u]of h.get(`${a+r},${l+c}`)||[]){if(t>=p)continue;const a=Ba(o,u);a<=2e-7||a{const i=a[(s+1)%a.length];return t+(e[0]*i[1]-i[0]*e[1])},0))/2,r=(s||1)/i,l=n*r*r;return lt.removed).map(t=>t.binding)),c=new Set;for(const t of s)t.id!==n&&("device"===t.bindingKind&&t.bindingRef&&c.add(`device:${t.bindingRef}`),"entity"===t.bindingKind&&t.bindingRef&&c.add(`entity:${t.bindingRef}`));const d=new Set;for(const t of s)"device"===t.bindingKind&&t.name&&d.add(`${t.name.trim()}|${t.area||""}`);const p=[];for(const t of Object.values(e?.devices||{})){if(!t||"service"===t.entry_type)continue;const e=`device:${t.id}`;if(c.has(e))continue;const s=String(t.name_by_user||t.name||t.id).trim();e!==a&&!h.has(e)&&d.has(`${s}|${t.area_id||""}`)||p.push({value:e,label:s,sub:(t.model||r.device)+("Group"===t.model?r.z2mGroup:""),kind:"device",ref:t.id,areaId:t.area_id||"",model:t.model||""})}for(const[t,s]of Object.entries(e?.entities||{})){const i=`entity:${t}`;if(c.has(i))continue;if(B(e,t,l)&&!h.has(i))continue;const o=tn.has(s?.platform),a="group"===s?.platform;if(!o&&!a)continue;if(s?.hidden&&!h.has(i))continue;const n=e?.states?.[t];p.push({value:i,label:s?.name||n?.attributes?.friendly_name||t,sub:`${t.split(".")[0]} · ${a?r.group:r.helper}`,kind:"entity",ref:t,areaId:s?.area_id||s?.device_id&&e?.devices?.[s.device_id]?.area_id||"",model:"",parentDeviceId:s?.device_id||void 0})}if(o){const t=new Set(p.map(t=>t.value));for(const[s,i]of Object.entries(e?.entities||{})){const o=`entity:${s}`;if(c.has(o)||t.has(o)||i?.hidden&&!h.has(o))continue;const a=!!i?.device_id&&l.devices.has(i.device_id);if(B(e,s,l)&&!h.has(o)&&!a)continue;const n=e?.states?.[s],d=i?.device_id?e?.devices?.[i.device_id]:null,u=d?String(d.name_by_user||d.name||""):"";p.push({value:o,label:i?.name||n?.attributes?.friendly_name||s,sub:`${s.split(".")[0]} · ${r.entity}${u?` · ${u}`:""}`,kind:"entity",ref:s,areaId:i?.area_id||d?.area_id||"",model:"",parentDeviceId:i?.device_id||void 0})}}return p.sort((t,e)=>t.label.localeCompare(e.label)||t.value.localeCompare(e.value))}const sn={kind:"active",enabledEntityIds:[],allEntityIds:[]};function on(t){return t.startsWith("entity:")?"entity":"device"}function an(t){const e=t.binding.indexOf(":"),s=e>=0?t.binding.slice(e+1):"",i=Object.keys(t).filter(t=>!["id","binding","hidden"].includes(t));return t.id===`h${s}`&&0===i.length}const nn=.001;function rn(t){return!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function ln(t){return`${t[0].toFixed(6)},${t[1].toFixed(6)}`}function hn(t,e,s){return Math.abs(t[0]-e[0])"room"!==t.kind&&s.some(s=>s!==t&&function(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>s))return!1;const n=i/a,r=o/a,l=e=>Math.abs((e[0]-t.a[0])*r-(e[1]-t.a[1])*n);if(l(e.a)>s||l(e.b)>s)return!1;const h=(e.a[0]-t.a[0])*n+(e.a[1]-t.a[1])*r,c=(e.b[0]-t.a[0])*n+(e.b[1]-t.a[1])*r;return Math.min(a,Math.max(h,c))-Math.max(0,Math.min(h,c))>s}(t,s,e))),o=i.map(t=>{const[e,s]=cn(t.a,t.b);return{a:e,b:s,key:`hidden|${pn(t,e,s)}`,sourceKind:t.kind,sourceId:t.id}}).sort((t,e)=>t.key.localeCompare(e.key)),a=i.flatMap(t=>[t.a,t.b].map((e,s)=>({point:[e[0],e[1]],key:`hidden|${dn(t)}|endpoint-${s}`,sourceKind:t.kind,sourceId:t.id}))).sort((t,e)=>t.key.localeCompare(e.key));return{segments:o,endpoints:a}}function gn(t){const e=t.epsilon??nn,s=t.roomCuts||[],i=new Map;for(const e of t.partitionCuts||[]){if("string"!=typeof e.hostId||!e.hostId||!rn(e.a)||!rn(e.b))continue;const t=i.get(e.hostId)||[];t.push([e.a[0],e.a[1],e.b[0],e.b[1]]),i.set(e.hostId,t)}const o=[];for(const[e,i]of W(t.space.rooms).entries())i.length<4||o.push({a:[i[0],i[1]],b:[i[2],i[3]],kind:"room",id:`room-edge-${e}`,cuts:s});for(const e of t.space.room_drafts||[])if(e.id!==t.activeDraftId)for(let t=0;t+1_n(s,t,e)))continue;const t=ln(s);n.has(t)||n.set(t,{point:[s[0],s[1]],key:t})}}}return{segments:[...a.values()].sort((t,e)=>t.key.localeCompare(e.key)),endpoints:[...n.values()].sort((t,e)=>t.key.localeCompare(e.key))}}function fn(t,e,s){return e.some(e=>rn(e)&&hn(t,e,s))}function bn(t,e,s){if(!s)return!0;const i=t-s.distance;return i<-1e-9||Math.abs(i)<=1e-9&&e.localeCompare(s.key)<0}function vn(t,e,s){const i=s.epsilon??nn,o=s.excludePoints||[],a=[...t.endpoints,...(s.extraEndpoints||[]).filter(t=>rn(t.point)).map(t=>({point:[t.point[0],t.point[1]],key:t.key}))],n=new Map;for(const t of a){if(fn(t.point,o,i))continue;const e=ln(t.point),s=n.get(e);(!s||t.key.localeCompare(s.key)<0)&&n.set(e,t)}return[...n.values()].map(t=>({kind:"endpoint",point:[...t.point],key:t.key,distance:Math.hypot(e[0]-t.point[0],e[1]-t.point[1])})).filter(t=>t.distance<=s.tolerance).sort((t,e)=>t.distance-e.distance||t.key.localeCompare(e.key))}function yn(t,e){if(!t.length)return null;if(e>0){const s=new Set;for(let i=0;i1)return{kind:"ambiguous",candidate:null,conflicts:t.filter(t=>s.has(t.key)).map(t=>({point:[...t.point],key:t.key}))}}return{kind:"resolved",candidate:t[0],conflicts:[]}}function $n(t,e,s){const i=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(i,o);if(!(a>0))return[...t.a];const n=i/a,r=o/a,l=Math.max(0,Math.min(a,(e[0]-t.a[0])*n+(e[1]-t.a[1])*r)),h=s>0?Math.max(0,Math.min(a,Math.round(l/s)*s)):l;return[t.a[0]+n*h,t.a[1]+r*h]}function wn(t,e,s){if(!(rn(e)&&rn(s.anchor)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??nn,o=function(t,e){const s=e[0]-t[0],i=e[1]-t[1];if(!(Math.hypot(s,i)>Number.EPSILON))return null;const o=Math.round(Math.atan2(i,s)/(Math.PI/4))*(Math.PI/4);return[Math.cos(o),Math.sin(o)]}(s.anchor,e);if(!o)return{kind:"none",candidate:null,conflicts:[]};const a=vn(t,e,s).filter(t=>function(t,e,s,i){const o=t[0]-e[0],a=t[1]-e[1];return o*s[0]+a*s[1]>i&&Math.abs(o*s[1]-a*s[0])<=i*Math.max(Math.hypot(o,a),1)}(t.point,s.anchor,o,i)),n=yn(a,s.distinguishTolerance||0);if(n)return n;let r=null;for(const a of t.segments){const t=a.b[0]-a.a[0],n=a.b[1]-a.a[1],l=a.a[0]-s.anchor[0],h=a.a[1]-s.anchor[1],c=o[0]*n-o[1]*t;let d=null;if(Math.abs(c)<=i*Math.max(Math.hypot(t,n),1)){if(Math.abs(l*o[1]-h*o[0])>i*Math.max(Math.hypot(l,h),1))continue;const t=[a.a,a.b].map(t=>({point:t,along:(t[0]-s.anchor[0])*o[0]+(t[1]-s.anchor[1])*o[1]})).filter(t=>t.along>i).sort((t,e)=>t.along-e.along);t.length&&(d=[...t[0].point])}else{const e=(l*n-h*t)/c,a=(l*o[1]-h*o[0])/c;e>i&&a>=-i&&a<=1+i&&(d=[s.anchor[0]+o[0]*e,s.anchor[1]+o[1]*e])}if(!d)continue;const p=Math.hypot(e[0]-d[0],e[1]-d[1]);p>s.tolerance||fn(d,s.excludePoints||[],i)||!bn(p,a.key,r)||(r={kind:"line",point:d,key:a.key,distance:p,segment:a})}return r?{kind:"resolved",candidate:r,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}const kn=.001;function xn(t,e,s,i){const o=Number.isFinite(t)&&t>0?Math.floor(t):0,a=t=>"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,n=a(s),r=a(i)??0,l=[];let h=null;for(let t=0;t=2&&Number.isFinite(t[0])&&Number.isFinite(t[1])}function Dn(t,e,s,i){return t*i-e*s}function Cn(t){let e=0;for(let s=0;st.localeCompare(e)),e[0]}function zn(t,e,s){const i=[...t];for(let t=!0;t&&i.length>=3;){t=!1;for(let o=0;o=0){i.splice(o,1),t=!0;break}}}return i}function An(t,e){const s=t.map(t=>Math.max(0,Math.min(1,t))).sort((t,e)=>t-e),i=[];for(const t of s)(!i.length||Math.abs(t-i[i.length-1])>e)&&i.push(t);return i}function Fn(t,e){return[t.a[0]+(t.b[0]-t.a[0])*e,t.a[1]+(t.b[1]-t.a[1])*e]}function Nn(t,e,s){const i=function(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o:0}(t,e);if(i<-s||i>1+s)return null;const o=Fn(e,i);return Math.hypot(o[0]-t[0],o[1]-t[1])<=s?Math.max(0,Math.min(1,i)):null}function Hn(t,e,s,i,o){const a=t.b[0]-t.a[0],n=t.b[1]-t.a[1],r=e.b[0]-e.a[0],l=e.b[1]-e.a[1],h=e.a[0]-t.a[0],c=e.a[1]-t.a[1],d=Dn(a,n,r,l),p=Math.max(Math.hypot(a,n),Math.hypot(r,l),1);if(Math.abs(d)>o*p){const t=Dn(h,c,r,l)/d,e=Dn(h,c,a,n)/d;return void(t>=-o&&t<=1+o&&e>=-o&&e<=1+o&&(s.push(Math.max(0,Math.min(1,t))),i.push(Math.max(0,Math.min(1,e)))))}if(!(Math.abs(Dn(h,c,a,n))>o*p)){for(const i of[e.a,e.b]){const e=Nn(i,t,o);null!=e&&s.push(e)}for(const s of[t.a,t.b]){const t=Nn(s,e,o);null!=t&&i.push(t)}}}function Ln(t){let e=t+1|0;return e^=e<<13,e^=e>>>17,e^=e<<5,e>>>0}function En(t,e){return t.minY-e.minY||t.index-e.index}function On(t){return t.subtreeMaxY=Math.max(t.item.maxY,t.left?.subtreeMaxY??-1/0,t.right?.subtreeMaxY??-1/0),t}function Bn(t){const e=t.right;return t.right=e.left,e.left=On(t),On(e)}function Wn(t){const e=t.left;return t.left=e.right,e.right=On(t),On(e)}function jn(t,e){return t?(En(e,t.item)<0?(t.left=jn(t.left,e),t.left.priority0)t.right=Gn(t.right,e);else{if(!t.left)return t.right;if(!t.right)return t.left;t.left.priority=e&&qn(t.left,e,s,i),t.item.minY<=s&&t.item.maxY>=e&&i.push(t.item),t.item.minY<=s&&qn(t.right,e,s,i))}function Un(t,e=.001){const s=Number.isFinite(e)&&e>0?e:kn,i=t.filter(t=>Mn(t.a)&&Mn(t.b)&&"string"==typeof t.key&&t.key.length>0&&Math.hypot(t.b[0]-t.a[0],t.b[1]-t.a[1])>s),o=i.map(()=>[0,1]);!function(t,e,s){const i=t.map((t,e)=>({index:e,minX:Math.min(t.a[0],t.b[0])-s,maxX:Math.max(t.a[0],t.b[0])+s,minY:Math.min(t.a[1],t.b[1])-s,maxY:Math.max(t.a[1],t.b[1])+s})),o=[...i].sort((t,e)=>t.minX-e.minX||t.minY-e.minY||t.index-e.index),a=[...i].sort((t,e)=>t.maxX-e.maxX||t.index-e.index),n=new Set;let r=null,l=0;for(const i of o){for(;lt.index-e.index);for(const a of o)Hn(t[a.index],t[i.index],e[a.index],e[i.index],s);n.add(i.index),r=jn(r,i)}}(i,o,s);const a=new Map;for(let t=0;t({key:t,a:e.a,b:e.b,sourceKeys:[...e.sourceKeys].sort((t,e)=>t.localeCompare(e))})).sort((t,e)=>t.key.localeCompare(e.key))}function Kn(t,e=.001){const s=Number.isFinite(e)&&e>0?e:kn,i=Un(t,s),o=new Map,a=new Map,n=new Map(i.map(t=>[t.key,t]));for(const t of i){const e=In(t.a,s),i=In(t.b,s);o.set(e,t.a),o.set(i,t.b),a.has(e)||a.set(e,new Set),a.has(i)||a.set(i,new Set),a.get(e).add(i),a.get(i).add(e)}const r=new Map;for(const[t,e]of a){const s=o.get(t);r.set(t,[...e].sort((t,e)=>{const i=o.get(t),a=o.get(e);return Math.atan2(i[1]-s[1],i[0]-s[0])-Math.atan2(a[1]-s[1],a[0]-s[0])||t.localeCompare(e)}))}const l=new Set,h=new Map,c=(t,e)=>`${t}>${e}`;for(const t of i){const e=[In(t.a,s),In(t.b,s)];for(const[t,a]of[e,[e[1],e[0]]]){if(l.has(c(t,a)))continue;const e=[],d=[];let p=t,u=a,_=!1;for(let s=0;s<=2*i.length+2;s++){const s=c(p,u);if(l.has(s)){_=p===t&&u===a;break}l.add(s),e.push(p),d.push(Tn(p,u));const i=r.get(u)||[],o=i.indexOf(p);if(o<0||!i.length)break;if(p=u,u=i[(o-1+i.length)%i.length],p===t&&u===a){_=!0;break}}if(!_||new Set(e).size<3||new Set(e).size!==e.length)continue;const m=e.map(t=>o.get(t)),g=Cn(m);if(!(g>s*s))continue;const f=Rn(zn(e,o,s)),b=new Set;for(const t of d)for(const e of n.get(t)?.sourceKeys||[])b.add(e);const v={ring:m,key:f,area:g,atomKeys:[...d],sourceKeys:[...b].sort((t,e)=>t.localeCompare(e))};h.has(f)||h.set(f,v)}}return{atoms:i,faces:[...h.values()].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))}}function Vn(t,e,s=.001){if(!Mn(e))return null;const i=t.faces.filter(t=>{if(t.ring.some((i,o)=>function(t,e,s,i){const o=s[0]-e[0],a=s[1]-e[1],n=o*o+a*a;if(!(n>0))return Math.hypot(t[0]-e[0],t[1]-e[1])<=i;const r=((t[0]-e[0])*o+(t[1]-e[1])*a)/n;return!(r<0||r>1)&&Math.hypot(e[0]+o*r-t[0],e[1]+a*r-t[1])<=i}(e,i,t.ring[(o+1)%t.ring.length],s)))return!1;let i=!1;for(let s=0,o=t.ring.length-1;se[1]!=n[1]>e[1]&&e[0]<(n[0]-a[0])*(e[1]-a[1])/(n[1]-a[1])+a[0]&&(i=!i)}return i});return[...i].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key))[0]||null}function Jn(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o,n=a>0?Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a)):0;return Math.hypot(e[0]+i*n-t[0],e[1]+o*n-t[1])}function Yn(t,e,s=.5){let i=Math.abs(t-e)%180;return i>90&&(i=180-i),i<=s}function Zn(t,e,s,i){const o=t.filter(t=>"outer"===t.kind&&!t.open&&t.cm>0).map(t=>({interval:t,reusePartitionId:e.find(e=>function(t,e,s){return!(Math.abs(t.cm-e.cm)>1e-6)&&(Yn(180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI,180*Math.atan2(e.b[1]-e.a[1],e.b[0]-e.a[0])/Math.PI,.001)&&Jn(e.a,t.a,t.b)<=s&&Jn(e.b,t.a,t.b)<=s)}(e,t,i))?.id||null})),a=new Map;for(const e of s){if("partition"===e.host?.kind)continue;const s=t.map(t=>{const s=180*Math.atan2(t.b[1]-t.a[1],t.b[0]-t.a[0])/Math.PI;return{interval:t,distance:Yn(e.angle,s)?Jn([e.x,e.y],t.a,t.b):1/0}}).filter(t=>t.distance<=i).sort((t,e)=>t.distance-e.distance||t.interval.key.localeCompare(e.interval.key)),o=s[0]?.interval;"outer"===o?.kind&&!o.open&&o.cm>0&&a.set(e.id,o.key)}return{materialize:o,openingIntervals:a,removeOpeningIds:[...a.keys()].sort((t,e)=>t.localeCompare(e))}}function Xn(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;return o>0?Math.max(0,Math.min(1,((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o)):0}const Qn=t=>!t.key.startsWith("static:room|");function tr(t,e){const s=e.b[0]-e.a[0],i=e.b[1]-e.a[1],o=s*s+i*i;if(!(o>0))return null;const a=((t[0]-e.a[0])*s+(t[1]-e.a[1])*i)/o,n=[e.a[0]+s*a,e.a[1]+i*a];return{point:n,t:a,distance:Math.hypot(n[0]-t[0],n[1]-t[1])}}function er(t,e){return e>0?Math.hypot(t[0]-Math.round(t[0]/e)*e,t[1]-Math.round(t[1]/e)*e):0}function sr(t,e,s){if(Qn(t.source)!==Qn(e.source))return Qn(t.source)?t:e;const i=er(t.point,s),o=er(e.point,s);return Math.abs(i-o)>1e-9?i>o?t:e:function(t,e){return t[0]-e[0]||t[1]-e[1]}(t.point,e.point)>0?t:e}function ir(t){return[t.from[0],t.from[1],t.to[0],t.to[1],t.targetKind].map(t=>"number"==typeof t?t.toFixed(6):t).join("|")}function or(t,e){return t.map(t=>t.key!==e.sourceKey?t:{...t,[e.endpoint]:[...e.to]})}function ar(t,e){const s=e.epsilon??.001;if(!(e.maxDistance>=0)||!e.point&&!e.requiredSourceKey)return{kind:"none"};const i=new Map,o=t.flatMap(t=>[{source:t,endpoint:"a",point:t.a},{source:t,endpoint:"b",point:t.b}]);for(let t=0;ts)||l>e.maxDistance)continue;const h=sr(n,r,e.gridStep||0),c=h===n?r:n;if(!Qn(h.source))continue;const d={sourceKey:h.source.key,endpoint:h.endpoint,from:[h.point[0],h.point[1]],to:[c.point[0],c.point[1]],targetSourceKey:c.source.key,targetKind:"endpoint",distance:l};i.set(ir(d),d)}for(const a of o)if(Qn(a.source))for(const o of t){if(o===a.source)continue;const t=tr(a.point,o);if(!t||t.t<=s||t.t>=1-s||!(t.distance>s)||t.distance>e.maxDistance)continue;const n={sourceKey:a.source.key,endpoint:a.endpoint,from:[a.point[0],a.point[1]],to:t.point,targetSourceKey:o.key,targetKind:"line",distance:t.distance};i.set(ir(n),n)}const a=[];for(const o of i.values()){const i=Kn(or(t,o),s),n=e.point?Vn(i,e.point,s):i.faces.find(t=>!e.requiredSourceKey||t.sourceKeys.includes(e.requiredSourceKey))||null;n&&a.push({proposal:o,face:n})}if(a.sort((t,e)=>t.proposal.distance-e.proposal.distance||ir(t.proposal).localeCompare(ir(e.proposal))),!a.length)return{kind:"none"};return new Set(a.map(t=>`${ir(t.proposal)}|${t.face.key}`)).size>1?{kind:"ambiguous",proposals:a.map(t=>t.proposal)}:{kind:"repair",...a[0]}}const nr=(t,e)=>(t.composedPath?.()||[t.target]).some(t=>(t=>!!t&&"function"==typeof t.matches)(t)&&e(t));class rr{constructor(t){this.host=t,this._openGroupId=null,this._groupGeneration=0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,this._globalDismissListening=!1,this._globalDismissGuard=t=>{this.handleOutsideDismiss(t)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(t){return this._openGroupId&&t.find(t=>t.id===this._openGroupId)||null}reset(){this._openGroupId=null,this._contentAnimation?.cancel(),this._contentAnimation=void 0,this._renderedContext="",this._focusOwned=!1,this._currentModel=null,this._blocked=!1,this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}closeForNavigation(){const t="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,e=!!this._openGroupId||!!t;this._openGroupId=null,t?.(),t&&(this._currentModel=null),this._clearDismissClick(),e&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(t,e){this._openGroupId!==e?t.some(t=>t.id===e)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=e,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(t){const e=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),t&&e&&this.host.updateComplete().then(()=>{const t=this.host.root(),s=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e&&null!==t.offsetParent);(s||t.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(t,e,s){const i=this._openGroupId===t.id,o=t.items.find(e=>e.id===t.activeItemId&&!e.disabled);return p``}renderGroupModel(t,e,s){const i=t.items.find(t=>!t.disabled)?.id,o=t.items.some(e=>e.id===t.activeItemId&&!e.disabled)?t.activeItemId:void 0;return{contextId:e,kind:"group",ariaLabel:s.openGroup(t.label),visibleLabel:t.label,content:p`
    this._groupKeydown(t)}> - ${t.items.map(e=>p``)} -
    `}}runContext(t,e,s){if(t===e)return s()}render(t,e){this._currentModel=t,this._blocked=e;const s=t?.kind||"hidden";return p`
    - -
    `}afterRender(){const t=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const e=t.querySelector(".editor-secondary"),s=this._renderedContext,i=e?.dataset.contextId||"";if(i&&s&&i!==s&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=e?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=i,s&&i!==s&&this._focusOwned){const s=i?e?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):t.querySelector(".editbar-tools");s?.focus?.(),this._focusOwned=!!i}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const e=[...t.querySelectorAll("[data-editor-palette]")].find(t=>t.dataset.editorPalette===this._currentModel?.launcherId);e&&null!==e.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const o=[...t.querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===this._openGroupId);o&&null!==o.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(t){if(nr(t,t=>t.hasAttribute("data-editor-navigation")))return"pointerdown"===t.type?this.closeForNavigation():"click"===t.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===t.type&&this._dismissPointerTarget){return!!(t.composedPath?.()||[t.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),t.preventDefault(),t.stopImmediatePropagation(),!0)}const e=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===t.type&&(this._openGroupId||e)){const s=nr(t,t=>t.classList.contains("editor-secondary")),i=nr(t,t=>!!t.dataset.editorGroup),o=nr(t,t=>!!t.dataset.editorPalette),a=nr(t,t=>t.classList.contains("stage"));if(!(s||i||o||"stay-open-on-canvas"===e?.dismissPolicy&&a))return t.preventDefault(),t.stopImmediatePropagation(),this._dismissPointerTarget=t.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):e?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(t,e,s){if(t.id!==this._openGroupId)return;const i=t.items.find(t=>t.id===e);if(!i||i.disabled)return;const o="toggle"===i.role&&"stay-open"===i.closePolicy,a=0===s.detail;o||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),i.invoke(),!o&&a&&this.host.updateComplete().then(()=>{const e=[...this.host.root().querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t.id&&null!==e.offsetParent);e?.focus()})}_groupKeydown(t){if(!["ArrowLeft","ArrowRight","Home","End"].includes(t.key))return;const e=[...t.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!e.length)return;t.preventDefault();const s=e.indexOf(this.host.root().activeElement),i="Home"===t.key?0:"End"===t.key?e.length-1:"ArrowLeft"===t.key?s<=0?e.length-1:s-1:(s+1)%e.length;e[i]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const t=!this._blocked&&"palette"===this._currentModel?.kind,e=!!this._openGroupId||t||!!this._dismissPointerTarget;e!==this._globalDismissListening&&(this._globalDismissListening=e,e?(window.addEventListener("pointerdown",this._globalDismissGuard,!0),window.addEventListener("click",this._globalDismissGuard,!0)):(window.removeEventListener("pointerdown",this._globalDismissGuard,!0),window.removeEventListener("click",this._globalDismissGuard,!0)))}}const lr=t=>t.stopPropagation(),hr=1e-6*U,cr=6*U;function dr(t){return Number.isFinite(t)?Math.round(t/U)*U:t}const pr=(t,e,s,i)=>Math.hypot(s-t,i-e),ur=(t,e,s,i,o,a,n,r)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+n-(t+s))),Math.max(Math.abs(a-e),Math.abs(a+r-(e+i)))),_r=(t,e,s,i,o,a,n)=>{const r=Math.max(Number(i)||0,0)/2,l=Math.PI/180,h=Math.cos(s*l)*r,c=Math.sin(s*l)*r,d=Math.cos(n*l)*r,p=Math.sin(n*l)*r,u=Math.max(pr(t+h,e+c,o+d,a+p),pr(t-h,e-c,o-d,a-p)),_=Math.max(pr(t+h,e+c,o-d,a-p),pr(t-h,e-c,o+d,a+p));return Math.min(u,_)},mr=t=>{const e=Number(t?.cell_cm);return e>0?e:5};function gr(t,e){const s=JSON.parse(JSON.stringify(t||[])),i=JSON.parse(JSON.stringify(e||{}));let o=0,a=0,n=0,r=0,l="",h=0,c=0,d=0;const p=(t,e)=>{Number.isFinite(t)&&Number.isFinite(e)&&t!==e&&Math.abs(e-t)<=hr&&c++},u=(t,e)=>{p(t[0],e[0]),p(t[1],e[1])},_={};let m=5;for(const t of s){const e=mr(t);null!=t?.id&&(_[String(t.id)]=e),e>m&&(m=e)}const g=(t,e,s,i=!1)=>{if(!(t>hr||i))return;o++,t>n&&(n=t);const a=t*K*e;a>r&&(r=a,l=s)};for(const t of s){const e=mr(t),s=null!=t?.id?String(t.id):"";for(const i of t.rooms||[]){a++;let t=0;if(i.poly?.length)i.poly=i.poly.map(e=>{const s=[dr(e[0]),dr(e[1])];return t=Math.max(t,pr(e[0],e[1],s[0],s[1])),u(e,s),s});else if(null!=i.x&&null!=i.y){const e=i.x,s=i.y,o=i.w||0,a=i.h||0,n=dr(e+o),r=dr(s+a),l=dr(e),h=dr(s),c=Math.max(U,n-l),d=Math.max(U,r-h);t=ur(e,s,o,a,l,h,c,d),i.x=l,i.y=h,i.w=c,i.h=d,p(e,l),p(s,h),p(e+o,l+c),p(s+a,h+d)}g(t,e,s)}for(const i of t.room_drafts||[]){a++;let t=0;const o=i.points||[],n=o.map(e=>{const s=[dr(e[0]),dr(e[1])];return t=Math.max(t,pr(e[0],e[1],s[0],s[1])),s}),r=n.length?[n[0]]:[],l=n.length?[[o[0],n[0]]]:[],h=[];for(let t=0;t+1=2)for(const[t,e]of l)u(t,e);g(t,e,s)}if(Array.isArray(t.room_drafts)){const e=t.room_drafts.length;t.room_drafts=t.room_drafts.filter(t=>t.points?.length>=2),d+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const i of t.partitions||[]){a++;const o=[i.a[0],i.a[1]],n=[i.b[0],i.b[1]],r=[dr(o[0]),dr(o[1])],l=[dr(n[0]),dr(n[1])];let h=Math.max(pr(o[0],o[1],r[0],r[1]),pr(n[0],n[1],l[0],l[1]));const c=pr(r[0],r[1],l[0],l[1]),d=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===i.id).every(t=>{const e=Number(t.length),s=Number(t.host.t),i=s*c;return Number.isFinite(e)&&e>0&&Number.isFinite(s)&&s>=0&&s<=1&&i-e/2>=-hr&&i+e/2<=c+hr});c>hr&&d?(i.a=r,i.b=l,u(o,r),u(n,l)):h=0,g(h,e,s)}for(const i of t.wall_columns||[]){a++;const t=[i.center[0],i.center[1]],o=[dr(t[0]),dr(t[1])],n=pr(t[0],t[1],o[0],o[1]);i.center=o,u(t,o),g(n,e,s)}for(const i of t.decor||[]){a++;let t=0;if("line"===i.kind){const e=[i.x1,i.y1],s=[i.x2,i.y2],o=[dr(e[0]),dr(e[1])],a=[dr(s[0]),dr(s[1])];t=Math.max(pr(e[0],e[1],o[0],o[1]),pr(s[0],s[1],a[0],a[1])),i.x1=o[0],i.y1=o[1],i.x2=a[0],i.y2=a[1],u(e,o),u(s,a)}else{const e=i.x,s=i.y,o=i.w,a=i.h,n=dr(e),r=dr(s);if(null!=i.w&&null!=i.h){const l=dr(e+o),h=dr(s+a),c=Math.max(U,l-n),d=Math.max(U,h-r);t=ur(e,s,o,a,n,r,c,d),i.w=c,i.h=d,p(e+o,n+c),p(s+a,r+d)}else t=pr(e,s,n,r);i.x=n,i.y=r,p(e,n),p(s,r)}g(t,e,s)}for(const i of t.openings||[]){if(a++,"partition"===i.host?.kind){const o=(t.partitions||[]).find(t=>t.id===i.host.id),a=Number(i.host.t);if(!o||!Number.isFinite(a)||a<0||a>1)continue;const n=o.b[0]-o.a[0],r=o.b[1]-o.a[1];if(Math.hypot(n,r)<=hr)continue;const l=o.a[0]+n*a,c=o.a[1]+r*a;let d=180*Math.atan2(r,n)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(i.angle),u=!(Number.isFinite(p)&&p===d),_=_r(i.x,i.y,Number.isFinite(p)?p:d,Number(i.length)||0,l,c,d);i.x=l,i.y=c,i.angle=d,u&&h++,g(_,e,s,u);continue}const o=q([i.x,i.y],t.rooms||[],cr,{step:U,length:Number(i.length)||0});if(!o)continue;const n=Number(i.angle),r=!(Number.isFinite(n)&&n===o.angle),l=_r(i.x,i.y,Number.isFinite(n)?n:o.angle,Number(i.length)||0,o.x,o.y,o.angle);i.x=o.x,i.y=o.y,i.angle=o.angle,r&&h++,g(l,e,s,r)}}for(const[t,e]of Object.entries(i)){if(!e||"object"!=typeof e)continue;const s=e;if("number"!=typeof s.x||"number"!=typeof s.y)continue;a++;const o=dr(s.x),n=dr(s.y),r=pr(s.x,s.y,o,n);i[t]={...s,x:o,y:n},p(s.x,o),p(s.y,n);const l="string"==typeof s.s?s.s:"";g(r,_[l]??m,l)}return{spaces:s,layout:i,report:{moved:o,coordsCanonicalized:c,total:a,maxShift:n,maxShiftCm:r,maxSpace:l,rotated:h,removedDrafts:d},changed:o>0||c>0}}function fr(t,e){const s=e.base+t*e.span;return Math.min(1,Math.max(0,s))}const br=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),vr=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1]);function yr(t,e,s){const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(a<=0)return vr(t,e);let n=((t[0]-e[0])*i+(t[1]-e[1])*o)/a;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))}function $r(t,e,s,i,o){for(const i of e)if(i&&!s.has(i.id)&&br(i.a)&&br(i.b)&&(vr(t,i.a)<=o||vr(t,i.b)<=o))return!0;for(const e of i?.roomPolygons||[])for(let s=0;s0&&r>0))return null;if(Math.abs(o[0]/n*(a[1]/r)-o[1]/n*(a[0]/r))>s)return null;for(const[s,o]of[[t.a,e.a],[t.a,e.b],[t.b,e.a],[t.b,e.b]])if(vr(s,o)<=i)return[(s[0]+o[0])/2,(s[1]+o[1])/2];return null}function kr(t,e){const s=.05*(Number(e.pitch)>0?Number(e.pitch):1),i=new Map;let o=(t||[]).filter(t=>!!t&&"string"==typeof t.id&&br(t.a)&&br(t.b));if(o.length<2)return{partitions:[...t||[]],merged:0,openingMoves:[]};const a=e.seedIds&&e.seedIds.length?new Set(e.seedIds):null;let n=0;for(let t=0;tvr(e,t.at)>s);let c=h[0]??r.a,d=h[h.length-1]??l.b;if(h.length<2&&(c=r.a,d=l.b),d[0]0?(d[0]-c[0])/u:0,m=u>0?(d[1]-c[1])/u:0,g=t=>u>0?((t[0]-c[0])*_+(t[1]-c[1])*m)/u:0;for(const t of[r,l]){const e=g(t.a),s=g(t.b)-e;for(const[o,a]of i)a.toId===t.id&&i.set(o,{fromId:a.fromId,toId:p.id,base:e+a.base*s,span:a.span*s});i.set(t.id,{fromId:t.id,toId:p.id,base:e,span:s})}a&&a.add(p.id),o=o.filter((e,s)=>s!==t.i&&s!==t.j),o.splice(t.i,0,p),n++}return{partitions:o,merged:n,openingMoves:[...i.values()]}}function xr(t,e,s,i){if(!t?.length||!s.length)return 0;const o=new Map(s.map(t=>[t.fromId,t]));let a=0;for(const s of t){const t=s?.host;if(!t||"partition"!==t.kind)continue;const n=o.get(t.id);if(!n)continue;s.host={...t,id:n.toId,t:fr(t.t,n)},a++;const r=J(s,e,i.coordScale,i.cellCm,i.gridPitch).resolved;r&&Object.assign(s,Y(s,r,i.coordScale))}return a}function Sr(t,e){const s=e?.excludeDraftId||null;return{roomPolygons:(t?.rooms||[]).map(t=>V(t)).filter(t=>!!t),columns:t?.wall_columns||[],draftEnds:(t?.room_drafts||[]).flatMap(t=>{if(s&&t?.id===s)return[];const e=t?.points||[];return e.length?[e[0],e[e.length-1]]:[]})}}const Mr=t=>!!t&&t.length>=2&&Number.isFinite(t[0])&&Number.isFinite(t[1]),Dr=(t,e)=>t[0]-e[0]||t[1]-e[1],Cr=(t,e,s,i)=>[t[0]+e*i,t[1]+s*i],Pr=(t,e,s,i)=>(t[0]-e[0])*s+(t[1]-e[1])*i,Ir=(t,e,s,i,o)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o))return null;const l=a/r,h=n/r,c=e=>Math.abs((e[0]-t[0])*h-(e[1]-t[1])*l);if(c(s)>o||c(i)>o)return null;const d=Pr(s,t,l,h),p=Pr(i,t,l,h),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>o?{lo:u,hi:_}:null},Tr=(t,e,s)=>Math.min(t.hi,e.hi)-Math.max(t.lo,e.lo)>s,Rr=t=>{const e=new Set(["id","a","b","cm"]);return Object.keys(t||{}).every(t=>e.has(t))},zr=(t,e,s,i)=>{const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],n=Math.hypot(o,a);if(!(n>i))return!0;const r=o/n,l=a/n,h=nt(t.cm,s.cellCm,s.gridPitch)/2;return e.some(e=>{const o=e?.center;if(!Mr(o))return!0;const a=Pr(o,t.a,r,l),c=Math.abs((o[0]-t.a[0])*l-(o[1]-t.a[1])*r),d=nt(Number(e.cm),s.cellCm,s.gridPitch)*("square"===e.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||a>=-d-i&&a<=n+d+i&&c<=h+d+i})},Ar=(t,e,s)=>{if(t.host){const i=J(t,e,s.coordScale,s.cellCm,s.gridPitch).resolved;return i?{center:i.center,angle:i.angle,length:i.length}:null}const i=Number(t.x)*s.coordScale,o=Number(t.y)*s.coordScale,a=Number(t.angle),n=Number(t.length)*s.coordScale;return[i,o,a,n].every(Number.isFinite)&&n>0?{center:[i,o],angle:a,length:n}:null},Fr=(t,e,s)=>{const i=t.angle*Math.PI/180,o=Math.cos(i),a=Math.sin(i),n=e.angle*Math.PI/180,r=Math.cos(n),l=Math.sin(n);if(Math.abs(o*l-a*r)>1e-6)return!1;const h=e.center[0]-t.center[0],c=e.center[1]-t.center[1];if(Math.abs(h*a-c*o)>s)return!1;const d=h*o+c*a;return Math.max(-t.length/2,d-e.length/2)({...t,a:[t.a[0]*e,t.a[1]*e],b:[t.b[0]*e,t.b[1]*e]}),Hr=(t,e,s,i)=>{const o=`~r-${(t=>{let e=2166136261;for(let s=0;s>>0;return e.toString(36)})(`${t}|${e[0].toFixed(9)},${e[1].toFixed(9)}|${s[0].toFixed(9)},${s[1].toFixed(9)}`)}`,a=`${t.slice(0,Math.max(1,64-o.length))}${o}`;let n=a;for(let t=2;i.has(n);t++){const e=`-${t}`;n=`${a.slice(0,64-e.length)}${e}`}return i.add(n),n},Lr=(t,e)=>{const s=[];for(const i of t){const t=s[s.length-1];!(t&&Math.abs(t.hi-i.lo)<=e&&t.safe===i.safe)||i.safe&&t.signature!==i.signature?s.push({lo:i.lo,hi:i.hi,a:i.a,b:i.b,safe:i.safe,signature:i.signature,roomIds:i.roomIds,finalCm:i.finalCm}):(t.hi=i.hi,t.b=i.b)}return s};function Er(t,e,s,i,o){const a=s||[],n=Array.isArray(t?.partitions)?t.partitions:[],r=Array.isArray(t?.openings)?t.openings:[],l=Array.isArray(t?.room_drafts)?t.room_drafts:[],h={walls:a,partitions:n,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!n.length&&!l.length)return h;const c=Math.max(2e-4*o.gridPitch,1e-9);let d=a,p=n,u=r;const _=Z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),m=((t,e,s,i)=>{const o=new Map(t.map(t=>[t.id,t])),a=new Set;for(const t of e){if(!o.get(t.id)||!Array.isArray(t.points)||t.points.length<2||t.segments.length!==t.points.length-1)continue;let e=!0;for(let o=0;o+10)){e=!1;break}const l=Math.hypot(n[0]-a[0],n[1]-a[1]);if(!(l>i)){e=!1;break}const h=s.flatMap(t=>{const e=t.cm>0?t.cm:X;if(t.open||"outer"!==t.kind&&"shared"!==t.kind||e+it.lo-e.lo||t.hi-e.hi);let c=0;for(const t of h){if(t.lo>c+i)break;if(c=Math.max(c,t.hi),c>=l-i)break}c!m.has(t.id));let f=0,b=0;const v=new Map(e.partitions.map(t=>[t.id,t]));for(const t of[...n].sort((t,e)=>t.id.localeCompare(e.id))){if(!p.some(e=>e.id===t.id))continue;if(!Rr(t))continue;const s=v.get(t.id);if(!(s&&Mr(s.a)&&Mr(s.b)&&Number.isFinite(s.cm)&&s.cm>0))continue;const[a,n]=Dr(s.a,s.b)<=0?[[s.a[0],s.a[1]],[s.b[0],s.b[1]]]:[[s.b[0],s.b[1]],[s.a[0],s.a[1]]],r=n[0]-a[0],l=n[1]-a[1],h=Math.hypot(r,l);if(!(h>c))continue;const _=r/h,g=l/h,y=Z(e.rooms,d,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),$=y.flatMap(t=>{if(t.open||"outer"!==t.kind&&"shared"!==t.kind)return[];const e=Ir(a,n,t.a,t.b,c);return e?[{interval:t,range:e}]:[]});if(!$.length)continue;const w=u.filter(t=>"partition"===t.host?.kind&&t.host.id===s.id).map(t=>({opening:t,resolved:J(t,e.partitions,o.coordScale,o.cellCm,o.gridPitch).resolved}));if(w.some(t=>!t.resolved))continue;const k=[0,h];for(const{range:t}of $)k.push(t.lo,t.hi);const x=k.slice(),S=new Map;for(const{opening:t,resolved:e}of w){const s=Pr(e.center,a,_,g),i={lo:s-e.length/2,hi:s+e.length/2};S.set(t.id,i),x.push(Math.max(0,i.lo),Math.min(h,i.hi))}const M=[...new Set(x.filter(t=>Number.isFinite(t)&&t>=-c&&t<=h+c).map(t=>Math.max(0,Math.min(h,t)).toFixed(9)))].map(Number).sort((t,e)=>t-e),D=p.map(t=>Nr(t,o.coordScale)).filter(t=>t.id!==s.id),C=e.room_drafts.filter(t=>!m.has(t.id)),P=[];for(let t=0;t+1c))continue;const r=Cr(a,_,g,i),l=Cr(a,_,g,n),h=$.filter(({range:t})=>t.lo<=i+c&&t.hi>=n-c).map(({interval:t})=>t),d=new Map;for(const t of h)d.set(t.roomId,t);const p=[...d.values()],u=new Set(p.map(t=>t.kind)),m=new Set(p.map(t=>t.cm>0?t.cm:X)),f=p[0]?.kind,b=[...d.keys()].sort(),v=1===u.size&&1===m.size&&("outer"===f&&1===b.length||"shared"===f&&2===b.length),y={id:s.id,a:r,b:l,cm:s.cm},w=D.some(t=>!!Ir(r,l,t.a,t.b,c)),k=C.some(t=>t.points.some((e,s)=>s+10?p[0].cm:X:0,I=v?Math.max(S,s.cm):s.cm;P.push({lo:i,hi:n,a:r,b:l,safe:x,signature:x?`${f}|${b.join(",")}|${I}`:"",roomIds:b,roomCm:S,finalCm:I})}if(!P.some(t=>t.safe))continue;const I=new Map;for(const{opening:t,resolved:e}of w){const i=S.get(t.id),a=k.some(t=>t>i.lo+c&&tTr(t,i,c)),r=new Set(n.filter(t=>t.safe).map(t=>t.signature));if(!a&&n.length>0&&n.every(t=>t.safe)&&1===r.size&&Q(e,y,c)){I.set(t.id,{resolved:e,signature:[...r][0]});continue}const l=tt(s,o.cellCm,o.gridPitch),h={lo:i.lo-l,hi:i.hi+l};for(const t of P)Tr(t,h,c)&&(t.safe=!1,t.signature="")}let T=!0;for(;T;){T=!1;for(const[t,e]of[...I.entries()]){const i=S.get(t),a=P.filter(t=>Tr(t,i,c));if(a.length>0&&a.every(t=>t.safe)&&a.every(t=>t.signature===e.signature))continue;I.delete(t);const n=tt(s,o.cellCm,o.gridPitch),r={lo:i.lo-n,hi:i.hi+n};for(const t of P)Tr(t,r,c)&&(t.safe=!1,t.signature="");T=!0}}const R=Lr(P,c),z=R.filter(t=>t.safe),A=R.filter(t=>!t.safe);if(!z.length)continue;if(p.length-1+A.length>2e3)continue;let F=d;for(const t of z){const[e,i]=Dr(s.a,s.b)<=0?[t.a,t.b]:[t.b,t.a];F=et(F,e,i,t.finalCm,o.pitch,o.coordScale)}if(F=st(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale),F.length>500)continue;const N=new Set(p.map(t=>t.id));N.delete(s.id);const H=A.map((t,e)=>{const i=[t.a[0]/o.coordScale,t.a[1]/o.coordScale],a=[t.b[0]/o.coordScale,t.b[1]/o.coordScale],n=0===e?s.id:Hr(s.id,i,a,N);return N.add(n),{id:n,a:i,b:a,cm:s.cm}}),L=p.findIndex(t=>t.id===s.id),E=p.slice();E.splice(L,1,...H);const O=E.map(t=>Nr(t,o.coordScale)),B=new Map;let W=!0;for(const{opening:t,resolved:e}of w){const s=I.get(t.id);if(s){const e=Y(t,s.resolved,o.coordScale),{host:i,...a}=e;B.set(t.id,a);continue}const i=S.get(t.id),a=H[A.findIndex(t=>t.lo<=i.lo+c&&t.hi>=i.hi-c)],n=a&&O.find(t=>t.id===a.id);if(!a||!n){W=!1;break}const r=n.b[0]-n.a[0],l=n.b[1]-n.a[1],h=Math.hypot(r,l),d=Pr(e.center,n.a,r/h,l/h),p={...Y(t,e,o.coordScale),host:{kind:"partition",id:a.id,t:d/h}};if(!it(p,O,o.coordScale,o.cellCm,o.gridPitch).resolved){W=!1;break}B.set(t.id,p)}if(!W)continue;const j=u.map(t=>B.get(t.id)||t),G=[...I.keys()].map(t=>B.get(t)),q=G.map(t=>Ar(t,O,o));if(q.some(t=>!t))continue;let U=!1;for(let t=0;tt.id));for(const t of j){if(K.has(t.id))continue;const e=Ar(t,O,o);e&&q.some(t=>Fr(t,e,c))&&(U=!0)}if(U)continue;const V=ot(e.rooms,F,i,o.pitch,o.cellCm,o.gridPitch,o.coordScale);[...I.entries()].every(([t,e])=>{const s=B.get(t),i=at(V,{x:s.x*o.coordScale,y:s.y*o.coordScale,angle:s.angle,length:s.length*o.coordScale},!0),a=[i.negative,i.positive].filter(t=>!!t?.full),n=e.signature.split("|")[1].split(","),r=new Set(a.map(t=>t.roomId));return a.length===n.length&&r.size===n.length&&n.every(t=>r.has(t))})&&(d=F,p=E,u=j,f+=z.length,b+=I.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:b,removedDrafts:m.size}}const Or=t=>JSON.parse(JSON.stringify(t)),Br=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Wr=(t,e)=>e.length>0&&e.length<=35&&("space"===t?/^[a-z0-9_-]+$/.test(e):/^[A-Za-z0-9_-]+$/.test(e));function jr(t,e){let s=String(e??"");const i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`^${i}_(.+)_([0-9a-f]{8})$`);let a=0;for(;a<16;a++){const t=o.exec(s);if(!t)return{root:s,layers:a,bounded:!1};s=t[1]}return{root:s,layers:a,bounded:o.test(s)}}const Gr=(t,e,s)=>{const i=t.get(e)||[];i.push(s),t.set(e,i)};const qr=1e-9;class Ur extends Error{constructor(t,e=""){super(e?`${t}: ${e}`:t),this.reason=t,this.name="WallSegmentModelError"}}const Kr=t=>JSON.parse(JSON.stringify(t)),Vr=t=>Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1])),Jr=t=>[Number(t[0]),Number(t[1])],Yr=t=>`${Number(t[0]).toFixed(12)},${Number(t[1]).toFixed(12)}`,Zr=(t,e)=>{const s=Yr(t),i=Yr(e);return sYr(t)<=Yr(e)?[Jr(t),Jr(e)]:[Jr(e),Jr(t)],Qr=(t,e)=>Math.hypot(e[0]-t[0],e[1]-t[1]),tl=t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],s=[];for(let i=1;i{const s=new Set,i=t=>{const e=Number(t);if(!Number.isFinite(e))return;const i=e/U;Math.abs(i-Math.round(i))>=ut&&s.add(e.toFixed(12))};for(const e of Array.isArray(t?.rooms)?t.rooms:[])for(const t of V(e)||[])i(t[0]),i(t[1]);for(const e of[t?.wall_segments,t?.walls])for(const t of Array.isArray(e)?e:[])i(t?.a?.[0]),i(t?.a?.[1]),i(t?.b?.[0]),i(t?.b?.[1]);for(const t of e)i(t?.[0]),i(t?.[1]);return s.size},sl=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;return a>1e-18?((t[0]-e[0])*i+(t[1]-e[1])*o)/a:0},il=(t,e,s)=>{const i=Math.max(0,Math.min(1,sl(t,e,s)));return Math.hypot(t[0]-(e[0]+(s[0]-e[0])*i),t[1]-(e[1]+(s[1]-e[1])*i))},ol=(t,e,s,i,o=qr)=>{const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(r<=o)return 0;const l=e=>Math.abs((e[0]-t[0])*n-(e[1]-t[1])*a)/r;if(l(s)>o||l(i)>o)return 0;const h=sl(s,t,e),c=sl(i,t,e);return Math.max(0,Math.min(1,Math.max(h,c))-Math.max(0,Math.min(h,c)))*r},al=t=>{const e=(new TextEncoder).encode(t),s=8*e.length,i=64*Math.ceil((e.length+9)/64),o=new Uint8Array(i);o.set(e),o[e.length]=128;const a=new DataView(o.buffer);a.setUint32(i-8,Math.floor(s/4294967296),!1),a.setUint32(i-4,s>>>0,!1);const n=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),r=new Uint32Array([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]),l=new Uint32Array(64),h=(t,e)=>t>>>e|t<<32-e;for(let t=0;t>>3,o=h(s,17)^h(s,19)^s>>>10;l[t]=l[t-16]+i+l[t-7]+o>>>0}let[e,s,i,o,c,d,p,u]=r;for(let t=0;t<64;t++){const a=u+(h(c,6)^h(c,11)^h(c,25))+(c&d^~c&p)+n[t]+l[t]>>>0,r=e&s^e&i^s&i;u=p,p=d,d=c,c=o+a>>>0,o=i,i=s,s=e,e=a+((h(e,2)^h(e,13)^h(e,22))+r>>>0)>>>0}r[0]=r[0]+e>>>0,r[1]=r[1]+s>>>0,r[2]=r[2]+i>>>0,r[3]=r[3]+o>>>0,r[4]=r[4]+c>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+u>>>0}const c=new Uint8Array(32),d=new DataView(c.buffer);for(let t=0;t{const e="abcdefghijklmnopqrstuvwxyz234567";let s=0,i=0,o="";for(const a of t)for(i=i<<8|a,s+=8;s>=5;)o+=e[i>>>s-5&31],s-=5;return s&&(o+=e[i<<5-s&31]),o},rl=(t,e,s,i,o="")=>{const[a,n]=Xr(e,s),r=`${t}|${Yr(a)}|${Yr(n)}|${[...i].sort().join(",")}${o}`;return`wall-${nl(al(r)).slice(0,20)}`};let ll=0;const hl=t=>{for(let e=0;e<1e3;e++){const e=globalThis.crypto?.randomUUID?.(),s=`wall-${e||nl(al(`${Date.now()}|${ll++}|${Math.random()}`)).slice(0,26)}`;if(!t.has(s))return s}throw new Ur("duplicate-id","id factory exhausted")},cl=t=>{const e=new Map;for(const s of Array.isArray(t?.wall_segments)?t.wall_segments:[])if(s&&"string"==typeof s.id&&Vr(s.a)&&Vr(s.b)){if(e.has(s.id))throw new Ur("duplicate-id",s.id);e.set(s.id,s)}return e},dl=(t,e,s)=>{const i=(s,i)=>{const o=t[0]-s[0],a=t[1]-s[1];return Math.abs(e[0]-i[0]-o)<=qr&&Math.abs(e[1]-i[1]-a)<=qr?[o,a]:null};return i(s.a,s.b)||i(s.b,s.a)},pl=(t,e)=>{const s=Array.isArray(t.rooms)?t.rooms:[],i=ct(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]),o=i.length?i:dt(s,null,1,.04*U,!0),a=[];for(const t of s){const s=V(t),i=Array.isArray(t?.wall_ids)?t.wall_ids:[];if(s&&i.length===s.length)for(let t=0;t"partition"!==t.host?.kind&&[Number(t.x),Number(t.y)].every(Number.isFinite)&&Number.isFinite(Number(t.angle))&&Number(t.length)>0&&o.some(e=>il([Number(t.x),Number(t.y)],[e[0],e[1]],[e[2],e[3]])<=.04*U)):[],l=r.map(t=>{const e=Number(t.angle)*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Number(t.length)/2,o=[Number(t.x),Number(t.y)];return{a:[o[0]-s[0]*i,o[1]-s[1]*i],b:[o[0]+s[0]*i,o[1]+s[1]*i]}}),h=(t,e)=>r.some(s=>{const i=[Number(s.x),Number(s.y)];if(!_t(t,e,Number(s.angle)))return!1;if(il(i,t,e)>.02*U)return!1;const o=Qr(t,e),a=sl(i,t,e)*o,n=Number(s.length)/2;return a+n>=-qr&&a-n<=o+qr}),c=new Map,d=[];for(const i of s){const r=String(i?.id||""),p=V(i);if(!r||!p||p.length<3)throw new Ur("invalid-room",r);const u=pt(s,r,n,U,1,[...t.walls||[],...l]);if(!u||u.poly.length<3)throw new Ur("invalid-room",r);const _=Array.isArray(i.wall_ids)?i.wall_ids:[],m=_.length===p.length;let g=null,f=m;if(f)for(let t=0;tqr||Math.abs(i[1]-g[1])>qr)){f=!1;break}g=i}const b=[];for(let t=0;t2)throw new Ur("third-owner",n);const d=m?_[u.parent[t]]:void 0;if("string"==typeof d&&d){const o=e.get(d);f||!o||ol(s,i,o.a,o.b)>qr?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:Jr(p[u.parent[t]]),b:Jr(p[(u.parent[t]+1)%p.length])})}const g=u.parent[t];l.parentKeys.add(ht(p[g],p[(g+1)%p.length],U));const v=[(s[0]+i[0])/2,(s[1]+i[1])/2],y=t=>t.some(t=>il(v,[t[0],t[1]],[t[2],t[3]])<=.04*U);l.zeroWall||=y(a)||y(o)&&!h(s,i),b.push(n)}const v={...i};delete v.wall_ids,d.push({...v,poly:u.poly.map(t=>[t[0],t[1]]),wall_ids:b})}return{atoms:[...c.values()].sort((t,e)=>t.key.localeCompare(e.key)),rooms:d}},ul=(t,e,s,i,o)=>{const a=new Map([...s.values()].map(t=>[Zr(t.a,t.b),t])),n=(t=>{const e=new Map;for(const s of t)"wall"===s.host?.kind&&e.set(s.host.id,(e.get(s.host.id)||0)+1);return e})(t.openings||[]),r=new Map;for(const t of e){const e=o?.get(t.key);if(e){const i=s.get(e);if(!i)throw new Ur("duplicate-id",e);r.set(t,i);continue}if(t.preferredIds.size>1)throw new Ur("duplicate-id",[...t.preferredIds].sort().join(","));const i=[...t.preferredIds][0],l=i?s.get(i):void 0;if(l){r.set(t,l);continue}const h=i?t.preferredCarriers.get(i):void 0;if(i&&h){r.set(t,{id:i,a:h.a,b:h.b,cm:0});continue}const c=a.get(t.key);if(c){r.set(t,c);continue}const d=[...s.values()].filter(e=>ol(t.a,t.b,e.a,e.b)>qr),p=[...new Map(d.map(t=>[t.id,t])).values()];if(p.sort((t,e)=>(n.get(e.id)||0)-(n.get(t.id)||0)||Qr(e.a,e.b)-Qr(t.a,t.b)||t.id.localeCompare(e.id)),p[0])r.set(t,p[0]);else if(1===t.positionalIds.size){const e=s.get([...t.positionalIds][0]);e&&r.set(t,e)}}const l=new Map;for(const[t,e]of r){const s=l.get(e.id)||[];s.push(t),l.set(e.id,s)}for(const[t,e]of l){const i=s.get(t)||r.get(e[0]);if(!i)throw new Ur("duplicate-id",t);const o=[(i.a[0]+i.b[0])/2,(i.a[1]+i.b[1])/2];e.sort((t,e)=>{const s=il(o,t.a,t.b)<=qr?0:1,a=il(o,e.a,e.b)<=qr?0:1;if(s!==a)return s-a;return(il(i.a,t.a,t.b)<=qr?0:1)-(il(i.a,e.a,e.b)<=qr?0:1)||t.key.localeCompare(e.key)}),e[0].id=t}const h=(t=>{const e=new Set;for(const s of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns])for(const t of Array.isArray(s)?s:[])"string"==typeof t?.id&&t.id&&e.add(t.id);for(const s of Array.isArray(t.room_drafts)?t.room_drafts:[])for(const t of Array.isArray(s?.segments)?s.segments:[])"string"==typeof t?.id&&t.id&&e.add(t.id);return e})(t);for(const t of e)if(t.id){if(h.has(t.id))throw new Ur("duplicate-id",t.id);h.add(t.id)}const c=e.filter(t=>!t.id);if(i){const e=c.map(e=>{const[s,i]=Xr(e.a,e.b),o=`${String(t.id||"")}|${Yr(s)}|${Yr(i)}|${[...e.owners].sort().join(",")}`,a=nl(al(o));return{atom:e,seed:o,digest:a,base:`wall-${a.slice(0,20)}`}}).sort((t,e)=>t.digest.localeCompare(e.digest)||t.atom.key.localeCompare(e.atom.key)),s=new Map;for(const t of e){const e=s.get(t.digest);if(e&&e!==t.seed)throw new Ur("duplicate-id",t.digest);s.set(t.digest,t.seed);let i=1,o=t.base;for(;h.has(o);)o=`${t.base}-${++i}`;t.atom.id=o,h.add(o)}}else for(const t of c)t.id=hl(h),h.add(t.id)},_l=(t,e)=>{if("partition"===t.host?.kind)return null;const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=e=>{if(!(Number(e.cm)>0))return!1;const i=sl(s,e.a,e.b),o=Qr(e.a,e.b),a=Number(t.length)/2;return i>=-qr&&i<=1+qr&&il(s,e.a,e.b)<=.02*U&&_t(e.a,e.b,Number(t.angle))&&Number.isFinite(a)&&a>=0&&i*o-a>=-qr&&i*o+a<=o+qr},o="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null,a=o&&i(o)?[o]:e.filter(i);if(1!==a.length)return null;const n=a[0];return{kind:"wall",id:n.id,t:Math.max(0,Math.min(1,sl(s,n.a,n.b)))}},ml=(t,e)=>{const s=[Number(t.x),Number(t.y)];if(!s.every(Number.isFinite))return null;const i=Number(t.length)/2,o=e.filter(e=>{if(!(Number(e.cm)>0))return!1;const o=sl(s,e.a,e.b),a=Qr(e.a,e.b);return o>=-qr&&o<=1+qr&&il(s,e.a,e.b)<=.02*U&&_t(e.a,e.b,Number(t.angle))&&Number.isFinite(i)&&i>=0&&o*a-i>=-qr&&o*a+i<=a+qr}),a=(e=>{if(!e.length)return null;const i="wall"===t.host?.kind?e.find(e=>e.id===t.host.id):null;return i||[...e].sort((t,e)=>il(s,t.a,t.b)-il(s,e.a,e.b)||Number(e.cm)-Number(t.cm)||(t.ide.id?1:0))[0]})(o);return a?{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,sl(s,a.a,a.b)))}:null},gl=(t,e,s)=>{const i=cl(t),{atoms:o,rooms:a}=pl(t,i);ul(t,o,i,e,s);const n=o.map(e=>{const s=e.id?i.get(e.id):void 0,o=((t,e,s)=>{if(e.zeroWall)return 0;const i=Array.isArray(t.walls)?t.walls:[],o=ht(e.a,e.b,U),a=i.filter(t=>Number(t.cm)>0&&(!(t.key!==o&&!e.parentKeys.has(t.key))||!(!Vr(t.a)||!Vr(t.b))&&ol(e.a,e.b,t.a,t.b)>=Qr(e.a,e.b)-qr)).map(t=>Math.max(1,Math.min(100,Number(t.cm))));if(new Set(a.map(t=>t.toFixed(9))).size>1)throw new Ur("thickness-conflict",e.key);if(a.length)return a[0];const n=mt(i,e.a,e.b,U,1);return n>0?n:Number(s?.cm)>0?Number(s.cm):0})(t,e,s);return{...s||{},id:e.id,a:[...e.a],b:[...e.b],cm:o}}),r=new Map(o.map(t=>[t.key,t.id]));for(const t of a)t.wall_ids=t.wall_ids.map(t=>r.get(t));t.rooms=a,t.wall_segments=n,t.walls=n.filter(t=>t.cm>0).map(t=>({key:ht(t.a,t.b,U),cm:t.cm,a:[...t.a],b:[...t.b]})),t.walls.length||delete t.walls,delete t.open_spans;for(const e of t.rooms)delete e.open_to;return((t,e)=>{const s=new Set;for(const e of[t.rooms,t.openings,t.decor,t.room_drafts,t.partitions,t.wall_columns,t.wall_segments])for(const t of Array.isArray(e)?e:[])"string"==typeof t?.id&&t.id&&s.add(t.id);for(const i of Array.isArray(t.room_drafts)?t.room_drafts:[]){const o=Array.isArray(i.segments)?i.segments:[];for(let a=0;a{const i=Array.isArray(t.openings)?t.openings:[];for(const t of i){if("partition"===t.host?.kind)continue;if(!t.host&&!s){const s=_l(t,e);s&&(t.host=s);continue}const i=_l(t,e)??(s?ml(t,e):null);if(i)t.host=i;else{if(!s)throw new Ur("opening-host",t.id);delete t.host}}})(t,n,e),n.reduce((t,e)=>t+(i.has(e.id)?0:1),0)};function fl(t,e={}){const s=JSON.stringify(t),i=Kr(t);lt(i);let o=0;const a=Number(i?.model_version||0)<9;for(const t of Array.isArray(i?.spaces)?i.spaces:[]){const s=e.lineageSpaceId===String(t?.id||"")?e.lineageHints:void 0;o+=gl(t,a,s)}return i.model_version=9,lt(i),{config:i,changed:s!==JSON.stringify(i),migratedSegments:o}}const bl=t=>!!t&&"object"==typeof t&&!Array.isArray(t);function vl(t,e){const s=(t,e)=>{if(Array.isArray(t)&&Array.isArray(e)){const i=new Map;for(const e of t)bl(e)&&"string"==typeof e.id&&e.id&&i.set(e.id,e);const o=e.map((e,o)=>{if(bl(e)&&"string"==typeof e.id&&e.id){const t=i.get(e.id);return t?s(t,e):Kr(e)}return o[e,e in t?s(t[e],i):Kr(i)]);for(const e of Object.keys(t))delete t[e];for(const[e,s]of i)t[e]=s;return t}return Kr(e)};return s(t,e)}const yl=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),$l=(t,e,s)=>Math.min(s,Math.max(e,t)),wl=t=>Pt({spaces:[t]})[0],kl=(t,e,s)=>{const i=s>0?s:1,o=t=>`${(t[0]/i).toFixed(12)},${(t[1]/i).toFixed(12)}`,a=o(t),n=o(e);return a0?n:1,l=Math.max(i*r*.02,1e-9),h=[];for(const e of t||[])for(const t of V(e)||[])h.push([t[0],t[1]]);const c=[];for(const t of s||[])Array.isArray(t)&&t.length>=4&&t.slice(0,4).every(Number.isFinite)&&c.push([t[0],t[1]],[t[2],t[3]]);const d=(t,e)=>e.some(e=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l),p=new Map;for(const n of t||[]){if(!n?.id)continue;const u=H(t,n.id,e,s,i,o,a,r);if(u)for(let t=0;t0&&_>0&&n===m&&_!==n))continue;const g=u.poly[i],f=u.poly[(i+1)%u.poly.length],b=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(b>l&&b<.5*a-1e-9*a))continue;if(d(g,c)||d(f,c))continue;if(d(g,h)&&d(f,h))continue;const v=kl(g,f,r),y=p.get(v);y?y.targets.add(n):p.set(v,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([n])})}}}let u=e.slice();const _=(t,e)=>Math.hypot(t[0]-e[0],t[1]-e[1])<=2*l,m=(t,e)=>{if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return!1;const s=[Number(t.a[0])*r,Number(t.a[1])*r],i=[Number(t.b[0])*r,Number(t.b[1])*r];return!![...s,...i].every(Number.isFinite)&&(_(s,e.a)&&_(i,e.b)||_(s,e.b)&&_(i,e.a))};for(const t of[...p.values()].filter(t=>1===t.targets.size).sort((t,e)=>kl(t.a,t.b,r).localeCompare(kl(e.a,e.b,r)))){const e=[...t.targets][0],s=u.filter(e=>m(e,t));if(new Set(s.map(t=>t.cm)).size>1)continue;let o=!1;u=u.map(s=>m(s,t)?(o=!0,s.cm===e?s:{...s,cm:e}):s),o||(u=et(u,t.a,t.b,e,i,r))}return u}const Sl=t=>{const e=[];for(const s of t||[]){const t=V(s);if(t?.length)for(let s=0;s"string"==typeof t?.id?t.id:"").filter(Boolean),c=new Set(h),d=new Map;for(const t of h){const e=jr("space",t);e.layers>0&&Wr("space",e.root)&&Gr(d,e.root,t)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const t of r){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s=new Map;for(const i of Array.isArray(t.rooms)?t.rooms:[]){const t="string"==typeof i?.id?i.id:"";if(!t)continue;const o=jr("room",t);o.layers>0&&Wr("room",o.root)&&Gr(s,o.root,t),u.has(t)||u.set(t,e),_.has(t)||_.set(t,String(i.name||""));const a="string"==typeof i.area?i.area:"";if(a){const s=m.get(a)||[];s.push({spaceId:e,roomId:t}),m.set(a,s)}}p.set(e,s)}const g=new Set(u.keys()),f=new Map,b=new Map;for(const t of l)"string"==typeof t?.id&&t.id&&(!0===t.removed?b.set(t.id,t):f.set(t.id,t));const v={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},y=new Set,$=t=>c.has(t)?null:(()=>{const e=jr("space",t).root;if(!Wr("space",e))return null;const s=d.get(e)||[];return 1===s.length?s[0]:null})(),w=(t,e)=>{const s=jr("room",t).root;if(!Wr("room",s))return null;const i=p.get(e)?.get(s)||[];return 1===i.length?i[0]:null},k=t=>{const e=s.effectiveAreaByMarker?.[t]||"",i=e&&m.get(e)||[];return 1===i.length?i[0]:null},x=(t,e,s)=>{if("string"==typeof t.room_id&&t.room_id&&!g.has(t.room_id)){const i=e?w(t.room_id,e):null;i?(t.room_id=i,v.roomRefsRemapped++):s?(t.room_id=s.roomId,v.roomRefsRemapped++):!0!==t.removed&&delete t.room_id}const i=t.vacuum?.segment_map;if(i&&"object"==typeof i&&!Array.isArray(i))for(const t of Object.keys(i)){const s=i[t];if("string"!=typeof s||!s||g.has(s))continue;const o=e?w(s,e):null;o?(i[t]=o,v.roomRefsRemapped++):v.nestedRefsUnresolved++}};for(const t of l){const e="string"==typeof t?.id?t.id:"";if(!e)continue;const s="string"==typeof t.space?t.space:"",i=s&&!c.has(s),a=o[e],n="string"==typeof a?.s?a.s:"",r=n&&!c.has(n),l=!0===t.removed;let h=s&&c.has(s)?s:null,d=null,p=!1;if(i){const i=$(s);i?(t.space=i,h=i,p=!0,v.spaceRefsRemapped++):l||"virtual"===t.binding?l||(delete t.space,h=null,v.markersDetached++):(d=k(e),d?(t.space=d.spaceId,h=d.spaceId,v.spaceRefsRemapped++):(delete t.space,h=null,v.markersDetached++))}l&&!p||x(t,h,d),r&&p&&n===s&&h&&(o[e]={...a,s:h},v.positionsRemapped++,y.add(e))}for(const t of Object.keys(o)){if(y.has(t))continue;const e=o[t],s="string"==typeof e?.s?e.s:"";if(!s||c.has(s))continue;const i=$(s);if(i){if(t.startsWith("rl_")){const s=t.slice(3),a=w(s,i);if(a){const s=`rl_${a}`;Br(o,s)||(o[s]={...e,s:i}),delete o[t],v.roomRefsRemapped++,v.positionsRemapped++;continue}}o[t]={...e,s:i},v.positionsRemapped++}}const S=s.ownerRoster,M=!0===S?.authoritative,D=new Set(S?.deviceIds||[]),C=new Set(S?.entityIds||[]),P=new Set(Array.isArray(i.settings?.known_devices)?i.settings.known_devices.filter(t=>"string"==typeof t):[]),I=(t,e="")=>{const s=String(S?.names?.[t]||e||"").trim();return s&&s!==t?s:""},T=(t,e,s,i="",o)=>({id:t,spaceId:e,kind:s,name:I(t,i),...o?{reason:o}:{}}),R=t=>{"room_label"===t?v.orphanRoomLabelsRemoved++:"group"===t?v.orphanGroupPositionsRemoved++:v.orphanDevicePositionsRemoved++},z=new Set;for(const[t,e]of Object.entries(o)){const i="string"==typeof e?.s?e.s:"";if(!i||c.has(i))continue;let a,n;const r=f.get(t),l=b.get(t);if(r)a=T(t,i,"device",String(r.name||"")),n="live";else if(l)a=T(t,i,"device",String(l.name||"")),n="absent";else if(t.startsWith("rl_")){const e=t.slice(3);a=T(t,i,"room_label",_.get(e)||""),n=g.has(e)?"live":"absent"}else if(t.startsWith("lg_")){const e=t.slice(3);a=T(t,i,"group"),n=C.has(e)?"live":M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")}else D.has(t)?(a=T(t,i,"device"),n="live"):P.has(t)?(a=T(t,i,"device"),n=M?"absent":"unverified","unverified"===n&&(a.reason="registry_unavailable")):(a=T(t,i,"unknown","","unknown_owner"),n="unverified");if("absent"!==n){if("live"===n){if(v.liveMissingPositions.push(a),!0===s.removeLiveMissingPositions){delete o[t],v.liveMissingPositionsRemoved++,R(a.kind),v.removedPositions.push(a);continue}}else v.unverifiedPositions.push(a);v.positionsUnresolved++,z.add(i)}else delete o[t],R(a.kind),v.removedPositions.push(a)}return v.deadSpaceIds=[...z].sort((t,e)=>t.localeCompare(e)),{config:i,layout:o,report:v,changed:JSON.stringify(i)!==a||JSON.stringify(o)!==n}}(t,e,s),n=a.config,r=gt(n,a.layout);lt(n),ft(a.layout);const l=JSON.stringify(t||{}),h=JSON.stringify(e||{}),c=Number.isInteger(Number(n.model_version))?Number(n.model_version):0,d=(t=>{let e=0,s=0,i=0;for(const s of t.markers||[]){if("ripple"===s.display&&(s.display="icon_ripple",e++),Array.isArray(s.controls)){const t="string"==typeof s.binding&&s.binding.startsWith("entity:")?s.binding.slice(7):"",i=t?s.controls.filter(e=>e!==t):s.controls;JSON.stringify(i)!==JSON.stringify(s.controls)&&(s.controls=i.length?i:null,e++)}const t=s.vacuum;t&&yl(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const o of t.spaces||[]){const t=o.settings;"glow"===t?.fill_mode&&("boolean"!=typeof t.glow_enabled&&(t.glow_enabled=!0),t.fill_mode="none",e++,s++);for(const t of o.rooms||[]){const s=t.settings;"glow"===s?.fill_mode&&("boolean"!=typeof s.glow&&(s.glow=!0),delete s.fill_mode,e++,i++)}if(yl(o,"segments")&&(delete o.segments,e++),yl(o,"plan_scale")){const t=Number(o.plan_scale);Number.isFinite(t)&&t>=kt&&t<=xt&&(yl(o,"plan_scale_x")||(o.plan_scale_x=t),yl(o,"plan_scale_y")||(o.plan_scale_y=t),delete o.plan_scale,e++)}const a=Number(o.cell_cm);yl(o,"cell_cm")&&(!Number.isFinite(a)||a<.1||a>1e3)&&(o.cell_cm=Number.isFinite(a)&&a>0?$l(a,.1,1e3):5,e++);const n=Number(o.cell_cm),r=Number.isFinite(n)&&n>0?n:5;for(const t of o.wall_columns||[])if("circle"===t.shape)yl(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&yl(t,"angle")){const s=((Number(t.angle)||0)%90+90)%90;t.angle!==s&&(t.angle=s,e++)}for(const t of o.decor||[]){if(yl(t,"width")){const s=Number(t.width);(yl(t,"width_cm")||Number.isFinite(s))&&(yl(t,"width_cm")||(t.width_cm=Number($l(s/$t*r,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(yl(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),yl(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number($l(St*Mt(t)/$t*r,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(yl(t,"scale")||yl(t,"size"))&&(delete t.scale,delete t.size,e++);let s=String(t.text??"");const i=[...s.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Dt(t[1]));if(!(yl(t,"entity")||yl(t,"attr")||yl(t,"unit")))continue;const o=String(t.unit??"").trim(),a="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||o?"":Ct(t.entity,a);if(i||n){if(n){const e=s.indexOf("{}");s=e>=0?s.slice(0,e)+n+s.slice(e+2):`${s}${s?" ":""}${n}`,t.text=s}delete t.entity,delete t.attr,delete t.unit,e++}}}return{total:e,glowSpaces:s,glowRooms:i}})(n);let p=d.total;const u=(_=n.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=gr(n.spaces||[],a.layout);let g=0,f=0,b=0,v="";const y=m.spaces.map(t=>{const e=bt(t);g+=e.report.wallsStraightened,f+=e.report.wallsStraightenSkipped;const s=Number(t?.cell_cm)>0?Number(t.cell_cm):5,i=e.report.maxStraightenShift/U*s;return i>b&&(b=i,v=String(t?.id||"")),e.space}),$=g?gr(y,m.layout):{...m,spaces:y};n.spaces=$.spaces;const w=g?{...m.report,moved:m.report.moved+$.report.moved,coordsCanonicalized:m.report.coordsCanonicalized+$.report.coordsCanonicalized,maxShift:Math.max(m.report.maxShift,$.report.maxShift),maxShiftCm:Math.max(m.report.maxShiftCm,$.report.maxShiftCm),maxSpace:$.report.maxShiftCm>m.report.maxShiftCm?$.report.maxSpace:m.report.maxSpace,rotated:m.report.rotated+$.report.rotated,removedDrafts:m.report.removedDrafts+$.report.removedDrafts}:{...m.report};let k=0,x=0,S=0,M=0,D=0,C=0,P=0,I=0;for(let t=0;t0===Number(t.cm)),walls:e.walls||[]}),a=wl(e),r=wl(s);if(!a||!r)continue;const l=Sl(a.rooms),h=Sl(r.rooms),c=.02*$t,d=(e.wall_segments||[]).filter(t=>0===Number(t.cm)).map(t=>({...t,key:ht(t.a,t.b,U),cm:0})),p=new Set(d.map(t=>t.key)),_=vt(e,a.rooms,E,c);x+=_.length;for(const t of _){const e=[t[0]/E,t[1]/E],s=[t[2]/E,t[3]/E],i=ht(e,s,U);p.has(i)||(p.add(i),d.push({key:i,a:e,b:s,cm:0}))}const m=d.length,g=yt(d,l,h,U,E),f=(t,e,s)=>{const i=s[0]-e[0],o=s[1]-e[1],a=i*i+o*o;if(!(a>0))return Math.hypot(t[0]-e[0],t[1]-e[1]);const n=Math.max(0,Math.min(1,((t[0]-e[0])*i+(t[1]-e[1])*o)/a));return Math.hypot(t[0]-(e[0]+i*n),t[1]-(e[1]+o*n))},b=g.filter(t=>{if(!Array.isArray(t.a)||!Array.isArray(t.b))return!1;const e=t.a.map(t=>t*E),s=t.b.map(t=>t*E);return h.some(([t,i])=>f(e,t,i)<=4*c&&f(s,t,i)<=4*c)});S+=Math.max(0,m-b.length);const v=(s.wall_segments||[]).filter(t=>Number(t.cm)>0);s.wall_segments=[...v,...b.map(({key:t,...e})=>e)];const y=b.map(t=>[t.a[0]*E,t.a[1]*E,t.b[0]*E,t.b[1]*E]),$=Array.isArray(e.walls)?e.walls.length:0;let w=yt(e.walls,l,h,U,E);w=xl(r.rooms,w,y,U,Number(s.cell_cm)>0?Number(s.cell_cm):5,$t,E),w=st(r.rooms,w,y,U,Number(s.cell_cm)>0?Number(s.cell_cm):5,$t,E),w=wt(w,s.rooms||[],U,1,y.map(t=>[t[0]/E,t[1]/E,t[2]/E,t[3]/E])),k+=Math.max(0,$-w.length),w.length?s.walls=w:delete s.walls;const T=kr(s.partitions||[],{pitch:U,geometry:Sr(s)});T.merged&&(M+=T.merged,s.partitions=T.partitions,xr(s.openings,s.partitions,T.openingMoves,{coordScale:E,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:$t}));const R=wl(s);if(R){const t=o(s,R,s.walls||[],y,{pitch:U,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,gridPitch:$t,coordScale:E});t.partitionsReconciled&&(D+=t.partitionsReconciled,C+=t.openingsRehosted,t.partitions.length?s.partitions=t.partitions:delete s.partitions,t.openings.length?s.openings=t.openings:delete s.openings,t.walls.length?s.walls=t.walls:delete s.walls),t.removedDrafts&&(P+=t.removedDrafts,t.roomDrafts.length?s.room_drafts=t.roomDrafts:delete s.room_drafts)}JSON.stringify({zero:(s.wall_segments||[]).filter(t=>0===Number(t.cm)),walls:s.walls||[]})!==i&&I++}let T=0;c<=9&&(T=function(t){const e=fl(t);return vl(t,e.config),{...e,config:t}}(n).migratedSegments);const R=lt(n),z=ft($.layout),A=JSON.stringify(R)!==l||JSON.stringify(z)!==h;c<9&&A&&(R.model_version=9);const F=JSON.stringify(R)!==l||JSON.stringify(z)!==h,N=Number.isInteger(Number(R.model_version))?Number(R.model_version):c,H=F?w:{...w,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},L=F?a.report:{...a.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:R,layout:z,report:{...H,modelFrom:c,modelTo:N,migrated:F?p:0,glowSpacesMigrated:F?d.glowSpaces:0,glowRoomsMigrated:F?d.glowRooms:0,canonicalized:F?I:0,wallSegmentsMigrated:F?T:0,legacyZeroWallsMigrated:F?x:0,wallsMerged:F?k:0,spansMerged:F?S:0,partitionsMerged:F?M:0,partitionsReconciled:F?D:0,openingsRehosted:F?C:0,redundantDraftsRemoved:F?P:0,wallsStraightened:F?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:F?b:0,maxStraightenSpace:F?v:"",latticeCoordinatesCanonicalized:F?r.canonicalized:0,latticeCoordinatesFar:F?r.far:0,latticeMaxShift:F?r.maxShift:0,latticeMaxShiftCm:F?r.maxShiftCm:0,latticeSpaces:F?r.spaces:[],...L},changed:F}}const Dl=new WeakSet,Cl=t=>{t._regSignature="",t._maybeRebuildDevices(),t.requestUpdate()};async function Pl(t,e,s,i,o,a=""){const n=t.host,r=n._serverCfg;if(!r)return!1;let l=JSON.parse(JSON.stringify(r));l.markers=l.markers||[];let h=l.markers.find(t=>t.id===e);if(!h){const t=n._devices.find(t=>t.id===e);if(!t||"device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return!1;h={id:t.id,binding:`${t.bindingKind}:${t.bindingRef}`,space:t.space||null,area:t.area||null,hidden:!!t.hidden},l.markers.push(h)}h.vacuum=function(t,e){const s=e.matrix.map(t=>Number(t.toFixed(6))),i=t?.map_routes;if(Array.isArray(i)&&i.length){const o=i.find(t=>t.id===e.routeId)||i.find(t=>t.source===e.source&&t.map_id===e.mapId);if(o)return{...t,map_routes:ko(i,o.id,s)}}return{...t,source:e.source,calibration:{...t?.calibration||{},[e.mapId]:s}}}(h.vacuum||{},{source:s,mapId:i,routeId:a,matrix:o}),l=t._prepareConfigCandidate(l);const c=u(r,l,n._cfgContentFingerprint,n._cfgRev,_);n._serverCfg=l,Cl(n),n._saveConfigDebounced.pending()&&n._saveConfigDebounced.cancel();try{return await t._saveConfigNow(c),!0}catch(t){return m(n,c,_),Cl(n),n._showToast(n._t("toast.cfg_save_failed",{err:n._errText(t)})),!1}}function Il(t){return 1===t}function Tl(t){return`${t}-${globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}`}function Rl(t){const e=function(t){return JSON.stringify({message:t.message.trim(),contact:t.contact.trim(),previewToken:t.attach&&t.preview?t.preview.token:""})}(t);return{idempotencyKey:t.submissionFingerprint&&t.submissionFingerprint!==e?Tl("report"):t.idempotencyKey,fingerprint:e}}function zl(t){return[...t].length}function Al(t,e=Date.now()){const s=t.message.trim();return s?zl(s)>1e4?"message_too_long":zl(t.contact.trim())>320?"contact_too_long":t.attach&&!t.preview?"preview_missing":t.attach&&t.preview.expiresAt<=e?"preview_expired":null:"message_required"}function Fl(t,e=Date.now()){return"building"!==t.status&&"sending"!==t.status&&"success"!==t.status&&null===Al(t,e)}function Nl(t){const e=function(t){const e=/Firefox\/(\d+)/i.exec(t);if(e)return{family:"firefox",major:Number(e[1])||0};const s=/(?:Chrome|Chromium|Edg)\/(\d+)/i.exec(t);if(s)return{family:"chromium",major:Number(s[1])||0};const i=/Version\/(\d+).+Safari\//i.exec(t);return i?{family:"webkit",major:Number(i[1])||0}:{family:"unknown",major:0}}(t.userAgent??globalThis.navigator?.userAgent??""),s=t.now??Date.now(),i=Number(t.registryLastSuccess||0),o=i<=0?"unknown":s-i<=6e5?"fresh":"stale",a=["en","ru","de","fr"].includes(t.language)?t.language:"en",n=globalThis.matchMedia?.bind(globalThis);return{browser_family:e.family,browser_major:Math.max(0,Math.min(999,e.major)),language:a,coarse_pointer:t.coarsePointer??!!n?.("(pointer: coarse)").matches,hover_capable:t.hoverCapable??!!n?.("(hover: hover)").matches,registry_access:"full"===t.registryAccess?"full":"limited"===t.registryAccess?"partial":"unavailable",registry_age_bucket:o}}function Hl(t){const e=t&&"object"==typeof t&&"code"in t?String(t.code||""):"";return new Set(["support_invalid_message","support_preview_expired","support_package_too_large","support_rate_limited","support_unavailable","support_rejected","unauthorized"]).has(e)?e:"support_unavailable"}const Ll=[{id:"air_conditioner",group:"appliance",art:{d:"M20.5 39h69a2.5 2.5 0 0 1 2.5 2.5V69a2.5 2.5 0 0 1-2.5 2.5h-69A2.5 2.5 0 0 1 18 69V41.5a2.5 2.5 0 0 1 2.5-2.5 M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v8.5H25z M25 63a2.5 2.5 0 0 1 2.5-2.5h55A2.5 2.5 0 0 1 85 63v3.5H25z",viewW:110,viewH:110}},{id:"armchair",group:"furniture",art:{d:"M42.5 29h25a10 10 0 0 1 10 10v26.5a10 10 0 0 1-10 10h-25a10 10 0 0 1-10-10V39a10 10 0 0 1 10-10 M77.5 48.5a6 6 0 0 1 2 11.657V71.5a4 4 0 0 1-4 4H34a4 4 0 0 1-4-4V60.157A6.001 6.001 0 0 1 32 48.5a6 6 0 0 1 5.419 3.424A4 4 0 0 1 38 54v7h33.5v-6.5q0-.107.005-.213A6 6 0 0 1 77.5 48.5 M42.75 57h24.5A4.75 4.75 0 0 1 72 61.75a4.75 4.75 0 0 1-4.75 4.75h-24.5A4.75 4.75 0 0 1 38 61.75 4.75 4.75 0 0 1 42.75 57M36.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 41.392 82H38a1.5 1.5 0 0 1-1.5-1.5zm36 0h-8l1.674 5.441A1.5 1.5 0 0 0 67.608 82H71a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"bathtub",group:"sanitary",art:{d:"M28 75h8l-1.674 5.441a1.5 1.5 0 0 1-1.434 1.059H29.5A1.5 1.5 0 0 1 28 80zm53 0h-8l1.674 5.441a1.5 1.5 0 0 0 1.434 1.059H79.5A1.5 1.5 0 0 0 81 80zM19.5 46.5H90a2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5H19.5A2.5 2.5 0 0 1 17 49a2.5 2.5 0 0 1 2.5-2.5 M19.5 51.5H90L86.633 68a10 10 0 0 1-9.798 8h-44.17a10 10 0 0 1-9.798-8zM30 36v-2a5 5 0 1 0-10 0v12.5",viewW:110,viewH:110}},{id:"bed",group:"furniture",art:{d:"M16 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 20.892 82H17.5a1.5 1.5 0 0 1-1.5-1.5zm78.5 0h-8l1.674 5.441A1.5 1.5 0 0 0 89.608 82H93a1.5 1.5 0 0 0 1.5-1.5zM25 29h60.5a2.5 2.5 0 0 1 2.5 2.5V68a2.5 2.5 0 0 1-2.5 2.5H25a2.5 2.5 0 0 1-2.5-2.5V31.5A2.5 2.5 0 0 1 25 29 M16 59.5c0-5.523 4.477-10 10-10h58.5c5.523 0 10 4.477 10 10v6H16zm-1 6h80.5a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H15a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1m15.5-20a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4h-21zm28.5 0a5 5 0 0 1 5-5h11a5 5 0 0 1 5 5v4H59z",viewW:110,viewH:110}},{id:"bidet",group:"sanitary",art:{d:"M36 57.5c0 5.523 4.477 10 10 10h18.5c5.523 0 10-4.477 10-10V53H36zm8.5 22a1 1 0 0 0 1 1H65a1 1 0 0 0 1-1v-12H44.5zm-8.5-32a1 1 0 0 1 1-1h36.5a1 1 0 0 1 1 1V53H36zM50.5 41a1 1 0 0 1 1-1H59a1 1 0 0 1 1 1v5.5h-9.5zM53 30a1 1 0 0 1 1-1h2.5a1 1 0 0 1 1 1v10H53zm-4 6a1 1 0 0 1-1-1v-2.5a1 1 0 0 1 1-1h4V36z",viewW:110,viewH:110}},{id:"boiler",group:"appliance",art:{d:"M46.5 81.5h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zm12.5 0h5V86a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1zM44 23h22.5a8 8 0 0 1 8 8v42.5a8 8 0 0 1-8 8H44a8 8 0 0 1-8-8V31a8 8 0 0 1 8-8 M36 31a8 8 0 0 1 8-8h22.5a8 8 0 0 1 8 8v42H36z M48.5 42.5a7 7 0 1 0 14 0 7 7 0 1 0-14 0m6.5 0 3.5-3.5",viewW:110,viewH:110}},{id:"chair",group:"furniture",art:{d:"M40.5 39.5C40.5 32.596 46.096 27 53 27h4c6.904 0 12.5 5.596 12.5 12.5v13a5 5 0 0 1-5 5h-19a5 5 0 0 1-5-5z M41.25 54h27.5A3.25 3.25 0 0 1 72 57.25a3.25 3.25 0 0 1-3.25 3.25h-27.5A3.25 3.25 0 0 1 38 57.25 3.25 3.25 0 0 1 41.25 54m28.25 6.5H64l1.88 21.63a1.5 1.5 0 0 0 1.495 1.37H68a1.5 1.5 0 0 0 1.5-1.5zm-29 0H46l-1.88 21.63a1.5 1.5 0 0 1-1.495 1.37H42a1.5 1.5 0 0 1-1.5-1.5z",viewW:110,viewH:110}},{id:"coffee_table",group:"furniture",art:{d:"M25 51h5.238l-1.534 15.646A1.5 1.5 0 0 1 27.211 68H26.5a1.5 1.5 0 0 1-1.5-1.5zm60.238 0H80l1.534 15.646A1.5 1.5 0 0 0 83.027 68h.711a1.5 1.5 0 0 0 1.5-1.5z M24.34 43.485A2.5 2.5 0 0 1 26.625 42h56.75a2.5 2.5 0 0 1 2.285 1.485l2.715 6.109A1 1 0 0 1 87.461 51H22.54a1 1 0 0 1-.914-1.406z M87.5 51h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"computer",group:"appliance",art:{d:"M24.5 38h40a2.5 2.5 0 0 1 2.5 2.5v22a2.5 2.5 0 0 1-2.5 2.5h-40a2.5 2.5 0 0 1-2.5-2.5v-22a2.5 2.5 0 0 1 2.5-2.5m50 0h11a2.5 2.5 0 0 1 2.5 2.5v29a2.5 2.5 0 0 1-2.5 2.5h-11a2.5 2.5 0 0 1-2.5-2.5v-29a2.5 2.5 0 0 1 2.5-2.5M43 65h5v7h-5zm-6 7h16m23-12h8m-8-17h8 M35 56V46h5m-5 7h5 M39.5 53a3.5 3.5 0 1 0 0-7m15.036 8.536a5 5 0 1 1 0-7.071",viewW:110,viewH:110}},{id:"cooktop",group:"appliance",art:{d:"M27.5 29h55a2.5 2.5 0 0 1 2.5 2.5v47a2.5 2.5 0 0 1-2.5 2.5h-55a2.5 2.5 0 0 1-2.5-2.5v-47a2.5 2.5 0 0 1 2.5-2.5 M33.5 66.5a8 8 0 1 0 16 0 8 8 0 1 0-16 0m26.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0m3 23a5 5 0 1 0 10 0 5 5 0 1 0-10 0m-29.5-23a8 8 0 1 0 16 0 8 8 0 1 0-16 0",viewW:110,viewH:110}},{id:"dining_table",group:"furniture",art:{d:"M83.5 43H78l6.742 30.82A1.5 1.5 0 0 0 86.207 75h.54a1.5 1.5 0 0 0 1.482-1.732zm-56 0H33l-6.742 30.82A1.5 1.5 0 0 1 24.793 75h-.54a1.5 1.5 0 0 1-1.482-1.732zm-2.62-7.406A2.5 2.5 0 0 1 27.21 34h56.58a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044A1 1 0 0 1 87.538 43H23.462a1 1 0 0 1-.932-1.362z M88.5 43h-66v3a1 1 0 0 0 1 1h64a1 1 0 0 0 1-1z",viewW:110,viewH:110}},{id:"dishwasher",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5M43 42h24m-1.5-11a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"dryer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5 M55.994 55.5s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m5.979-11s-1.329 1.81-1.479 3.143c-.198 1.761 1.599 2.554 1.48 4.321-.1 1.479-1.48 3.536-1.48 3.536m-8.519-11s-.886 1.81-.986 3.143c-.132 1.761 1.066 2.554.986 4.321-.067 1.479-.986 3.536-.986 3.536",viewW:110,viewH:110}},{id:"exercise",group:"other",art:{d:"m60.752 62.03-5.379-2.312 11.066-25.75a1.5 1.5 0 0 1 1.97-.786l2.624 1.127a1.5 1.5 0 0 1 .786 1.97zm-18.648-4.512-4.353 1.189-4.474-16.382a1.5 1.5 0 0 1 1.052-1.842l1.459-.398a1.5 1.5 0 0 1 1.842 1.052z M41.297 38.173a2.215 2.215 0 0 1 0 4.43H29.781a2.215 2.215 0 0 1 0-4.43zm33.219 38.091a2.657 2.657 0 1 1 0 5.315H28.453a2.657 2.657 0 1 1 0-5.315zm8.641-51.366a1.795 1.795 0 0 1 .68 2.41l-3.03 5.59a5 5 0 0 1-4.396 2.618H65.657a1.772 1.772 0 1 1 0-3.544h9.61a3 3 0 0 0 2.598-1.5l2.84-4.917a1.795 1.795 0 0 1 2.452-.657M29.339 65.787c0-6.699 6.457-11.503 12.873-9.579l24.291 7.288a10 10 0 0 1 7.127 9.578v3.19H29.339zm0 15.852h7.086l-1.394 3.486a1.5 1.5 0 0 1-1.393.943h-2.8a1.5 1.5 0 0 1-1.5-1.5zm44.291 0h-7.087l1.395 3.486a1.5 1.5 0 0 0 1.392.943h2.8a1.5 1.5 0 0 0 1.5-1.5z M35.54 66.077a4.872 4.872 0 1 0 9.743 0 4.872 4.872 0 1 0-9.744 0",viewW:110,viewH:110}},{id:"fireplace",group:"other",art:{d:"M26.672 34.425h55.99v39.47h-55.99Z M23 74.894a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v5.802a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zM23 29a1 1 0 0 1 1-1h61.334a1 1 0 0 1 1 1v4.425a1 1 0 0 1-1 1H24a1 1 0 0 1-1-1zm15.145 21.44a5 5 0 0 1 5-5h23.043a5 5 0 0 1 5 5v23.454H38.145z M44.57 65.633a2.754 2.754 0 0 1 2.754-2.753H62.01v5.507H47.324a2.754 2.754 0 0 1-2.754-2.754m20.194 5.507a2.754 2.754 0 0 0-2.754-2.753H47.324v5.507H62.01a2.754 2.754 0 0 0 2.754-2.754 M44.57 71.14a2.754 2.754 0 1 0 5.508 0 2.754 2.754 0 1 0-5.508 0m14.688-5.507a2.754 2.754 0 1 0 5.507 0 2.754 2.754 0 1 0-5.507 0",viewW:110,viewH:110}},{id:"fridge",group:"appliance",art:{d:"M35.5 18H75a2.5 2.5 0 0 1 2.5 2.5v64A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5v-64a2.5 2.5 0 0 1 2.5-2.5 M33 46.5h44.5v38A2.5 2.5 0 0 1 75 87H35.5a2.5 2.5 0 0 1-2.5-2.5zm2.5 44a1 1 0 0 0 1 1h37a1 1 0 0 0 1-1V87h-39zm3-49.5v-9.5m0 30.5v-9.5",viewW:110,viewH:110}},{id:"hood",group:"appliance",art:{d:"M44.308 57h21.384L83 69H27zM44 37.5a2.5 2.5 0 0 1 2.5-2.5h17a2.5 2.5 0 0 1 2.5 2.5V57H44zM80.5 74h-51a2.5 2.5 0 0 1-2.5-2.5V70a1 1 0 0 1 1-1h54a1 1 0 0 1 1 1v1.5a2.5 2.5 0 0 1-2.5 2.5",viewW:110,viewH:110}},{id:"kitchen_cabinet",group:"furniture",art:{d:"M38 88a1 1 0 0 0 1 1h32a1 1 0 0 0 1-1v-3H38zm17-68h16.5a2.5 2.5 0 0 1 2.5 2.5v17a2.5 2.5 0 0 1-2.5 2.5H55zm-19 2.5a2.5 2.5 0 0 1 2.5-2.5H55v22H38.5a2.5 2.5 0 0 1-2.5-2.5zM59 33v5m-8-5v5m4 47h16.5a2.5 2.5 0 0 0 2.5-2.5v-17a2.5 2.5 0 0 0-2.5-2.5H55zm-19-2.5a2.5 2.5 0 0 0 2.5 2.5H55V63H38.5a2.5 2.5 0 0 0-2.5 2.5zM67 67h-5m-14 0h-5",viewW:110,viewH:110}},{id:"kitchen_sink",group:"sanitary",art:{d:"M21.5 84h68a2.5 2.5 0 0 0 2.5-2.5v-35a2.5 2.5 0 0 0-2.5-2.5h-68a2.5 2.5 0 0 0-2.5 2.5v35a2.5 2.5 0 0 0 2.5 2.5 M26 78h26.5a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H26a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m23-24v5m9.5 19H85a1 1 0 0 0 1-1V51a1 1 0 0 0-1-1H58.5a1 1 0 0 0-1 1v26a1 1 0 0 0 1 1m-6-39.5a1 1 0 0 0-1-1H44a1 1 0 0 0-1 1V44h9.5zM58 33v-2a5 5 0 1 0-10 0v6.5M62 54v5",viewW:110,viewH:110}},{id:"nightstand",group:"furniture",art:{d:"M76.5 73.5h-8l1.674 5.441A1.5 1.5 0 0 0 71.608 80H75a1.5 1.5 0 0 0 1.5-1.5zm-42.5 0h8l-1.674 5.441A1.5 1.5 0 0 1 38.892 80H35.5a1.5 1.5 0 0 1-1.5-1.5z M34.5 30.5h41A2.5 2.5 0 0 1 78 33v40.5a2.5 2.5 0 0 1-2.5 2.5h-41a2.5 2.5 0 0 1-2.5-2.5V33a2.5 2.5 0 0 1 2.5-2.5 M32 39.5h46v18H32Zm18.5 9H60M50.5 66H60",viewW:110,viewH:110}},{id:"oven",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v36a2.5 2.5 0 0 1-2.5 2.5h-47A2.5 2.5 0 0 1 29 76V40a2.5 2.5 0 0 1 2.5-2.5 M40.5 47h29a2.5 2.5 0 0 1 2.5 2.5v20a2.5 2.5 0 0 1-2.5 2.5h-29a2.5 2.5 0 0 1-2.5-2.5v-20a2.5 2.5 0 0 1 2.5-2.5m25-16a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5M43 42h24",viewW:110,viewH:110}},{id:"plant",group:"other",art:{d:"M45.952 75.849a2.5 2.5 0 0 0 2.485 2.231H63.43a2.5 2.5 0 0 0 2.485-2.231l1.571-14.532H44.381zM60.236 44.78C58.423 36.174 66.805 29.605 77 31.87c0 8.155-8.609 16.763-16.763 12.912m-9.761 5.147c1.517-8.665-7.085-14.943-17.195-12.33.28 8.15 9.178 16.458 17.195 12.33 M55.706 55.88v-2.944m0 0c1.133-5.89 7.09-10.956 12.912-15.404M55.706 52.936c-4.938-4.146-7.982-6.16-14.044-9.061m1.453 12.005h25.636a1 1 0 0 1 1 1v3.437a1 1 0 0 1-1 1H43.115a1 1 0 0 1-1-1v-3.436a1 1 0 0 1 1-1",viewW:110,viewH:110}},{id:"rug",group:"other",art:{d:"M90.27 33.874h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.154h-5.622m-59.028 0H20m70.27 5.152h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20m70.27 5.153h-5.622m-59.028 0H20M28.12 32h54.025a2.5 2.5 0 0 1 2.5 2.5v40.439a2.5 2.5 0 0 1-2.5 2.5H28.12a2.5 2.5 0 0 1-2.5-2.5V34.5a2.5 2.5 0 0 1 2.5-2.5 M25.62 34.5a2.5 2.5 0 0 1 2.5-2.5h1.717v45.439H28.12a2.5 2.5 0 0 1-2.5-2.5zm59.026 0a2.5 2.5 0 0 0-2.5-2.5H80.43v45.439h1.716a2.5 2.5 0 0 0 2.5-2.5zM37.8 54.485l17.801-8.432 17.801 8.432-17.8 8.432z",viewW:110,viewH:110}},{id:"shelving",group:"furniture",art:{d:"M28 58.5h53V39H28Z M64 58.5h5V47a1 1 0 0 0-1-1h-3a1 1 0 0 0-1 1zM52.5 39h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-12 0h-5V27.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm-5 29.5V71a1 1 0 0 1 1 1H49a1 1 0 0 1 1-1v-2.5a1 1 0 0 1-1-1H36.5a1 1 0 0 1-1 1m33.5-10h7v-14a1 1 0 0 0-1-1h-5a1 1 0 0 0-1 1zM47.5 39h-7V25a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1zm-12 34v4a1 1 0 0 1 1 1h14a1 1 0 0 1 1-1v-4a1 1 0 0 1-1-1h-14a1 1 0 0 1-1 1M28 78h53v7H28Z M28 89.5V23a2.5 2.5 0 0 1 2.5-2.5h48A2.5 2.5 0 0 1 81 23v66.5",viewW:110,viewH:110}},{id:"shower",group:"sanitary",art:{d:"M31 84h49a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H31a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1m31.5-47.6v-4.686a11.7 11.7 0 0 0-3.441-8.283A11.77 11.77 0 0 0 50.75 20a11.77 11.77 0 0 0-8.309 3.431A11.7 11.7 0 0 0 39 31.714V84 M72 39.25a10.25 10.25 0 1 0-20.5 0zm-17.25 5v2.5m7.25-2.5v2.5m7.25-2.5v2.5m-14.5 3.75V53M62 50.5V53m7.25-2.5V53m-14.5 3.75v2.5m7.25-2.5v2.5m7.25-2.5v2.5M54.75 63v2.5M62 63v2.5m7.25-2.5v2.5",viewW:110,viewH:110}},{id:"sink",group:"sanitary",art:{d:"M30 57c0 5.523 4.477 10 10 10h29.5c5.523 0 10-4.477 10-10v-6.5A2.5 2.5 0 0 0 77 48H32.5a2.5 2.5 0 0 0-2.5 2.5zm14 22a1 1 0 0 0 1 1h19.5a1 1 0 0 0 1-1V67H44zm6-36.5a1 1 0 0 1 1-1h7.5a1 1 0 0 1 1 1V48H50zM45 37v-2a5 5 0 1 1 10 0v6.5",viewW:110,viewH:110}},{id:"sofa",group:"furniture",art:{d:"M59.5 29h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5m-34 0h24a5 5 0 0 1 5 5v32a5 5 0 0 1-5 5h-24a5 5 0 0 1-5-5V34a5 5 0 0 1 5-5 M18.5 44a5 5 0 0 1 5 5v12H86V49a5 5 0 0 1 5-5h.5a5 5 0 0 1 5 5v21.5a5 5 0 0 1-5 5H18a5 5 0 0 1-5-5V49a5 5 0 0 1 5-5z M25.25 57h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 25.25 57m34 0h24.5a4.75 4.75 0 0 1 4.75 4.75 4.75 4.75 0 0 1-4.75 4.75h-24.5a4.75 4.75 0 0 1-4.75-4.75A4.75 4.75 0 0 1 59.25 57M18.5 75.5h8l-1.674 5.441A1.5 1.5 0 0 1 23.392 82H20a1.5 1.5 0 0 1-1.5-1.5zm72 0h-8l1.674 5.441A1.5 1.5 0 0 0 85.608 82H89a1.5 1.5 0 0 0 1.5-1.5z",viewW:110,viewH:110}},{id:"stairs",group:"other",art:{d:"M73.47 46.365v-17.62m-10.571 25.55V36.674M52.326 61.783V45.484M42.193 69.273v-15.86M28.979 77.761a1 1 0 0 1 1-1h53.624v6.489a1 1 0 0 1-1 1H29.98a1 1 0 0 1-1-1z M26.777 57.497a1 1 0 0 1 1-1h2.846a1 1 0 0 1 1 1V83.25a1 1 0 0 1-1 1h-2.846a1 1 0 0 1-1-1z m73.113 28.633-41.492 32.93v-4.405l39.9-32.007a.7.7 0 0 1 .428-.151h3.724a1.762 1.762 0 1 1 0 3.524h-2.248a.5.5 0 0 0-.312.109M25.895 53.414a3.084 3.084 0 1 0 6.167 0 3.084 3.084 0 1 0-6.167 0m14.978 16.859a1 1 0 0 1 1-1h41.73v7.488h-42.73zm10.131-7.489a1 1 0 0 1 1-1h31.598v7.488H51.004z M61.576 55.294a1 1 0 0 1 1-1h21.026v7.49H61.576z M72.15 47.806a1 1 0 0 1 1-1h9.454a1 1 0 0 1 1 1v6.489H72.15z",viewW:110,viewH:110}},{id:"toilet",group:"sanitary",art:{d:"M32 26.5a2.5 2.5 0 0 1 2.5-2.5H45a2.5 2.5 0 0 1 2.5 2.5V57H32zM47.5 52h29a2.5 2.5 0 0 1 2.5 2.5V57H47.5zm17.037 20.102C73.95 70.288 79 62.01 79 57H32c.5 7.5 8.072 7 7 11l-2.342 16.862a1 1 0 0 0 .99 1.138H66.72a1 1 0 0 0 .97-1.243z",viewW:110,viewH:110}},{id:"tv",group:"appliance",art:{d:"M23.5 31H86a2.5 2.5 0 0 1 2.5 2.5v34A2.5 2.5 0 0 1 86 70H23.5a2.5 2.5 0 0 1-2.5-2.5v-34a2.5 2.5 0 0 1 2.5-2.5M50 70h9.5v8H50Zm-10 8h30M42.5 45.5h5m5 0h-5m0 0v9.75 m56.75 45.5 5 10 5-10",viewW:110,viewH:110}},{id:"wardrobe",group:"furniture",art:{d:"M28 23a2.5 2.5 0 0 1 2.5-2.5h24V85h-24a2.5 2.5 0 0 1-2.5-2.5zm2 65.5a1 1 0 0 0 1 1h47a1 1 0 0 0 1-1V85H30zM81 23a2.5 2.5 0 0 0-2.5-2.5h-24V85h24a2.5 2.5 0 0 0 2.5-2.5zM50.5 48v9.5m8-9.5v9.5",viewW:110,viewH:110}},{id:"washer",group:"appliance",art:{d:"M31.5 25h47a2.5 2.5 0 0 1 2.5 2.5v55a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5v-55a2.5 2.5 0 0 1 2.5-2.5 M31.5 37.5h47A2.5 2.5 0 0 1 81 40v42.5a2.5 2.5 0 0 1-2.5 2.5h-47a2.5 2.5 0 0 1-2.5-2.5V40a2.5 2.5 0 0 1 2.5-2.5 M38 60.5a17 17 0 1 0 34 0 17 17 0 1 0-34 0 M43.5 60.5a11.5 11.5 0 1 0 23 0 11.5 11.5 0 1 0-23 0m22-29.5a2 2 0 1 0 4 0 2 2 0 1 0-4 0m7.5 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0m-37.25 0h9.5",viewW:110,viewH:110}},{id:"work_table",group:"furniture",art:{d:"M88.5 47h-20v14h20zm-12 4h4m7.984-8h-66v4h66zm-66 4h4v26.5a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5z M24.866 35.597A2.5 2.5 0 0 1 27.197 34h56.577a2.5 2.5 0 0 1 2.33 1.594l2.35 6.044a1 1 0 0 1-.93 1.362H23.46a1 1 0 0 1-.933-1.361zM88.5 61h-20v12.5A1.5 1.5 0 0 0 70 75h17a1.5 1.5 0 0 0 1.5-1.5zm-12 4h4",viewW:110,viewH:110}}];class El{constructor(t,e){this.host=t,this.hooks=e}place(t,e="mouse"){const s=this.host._decorImagePalette,i=this.host._curSpaceCfg;if(!s||!i)return;const o=this.hooks.decorSnap(t,e),a=It(s.width,s.height),n=Tt(a.w,this.host._cellCm,this.host._gridPitch)/E,r=Tt(a.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,l="di"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),h=this.hooks.geometrySnapshot();i.decor=[...this.host._decorList,{id:l,kind:"image",asset_id:s.asset_id,x:A(o[0]/E-n/2),y:A(o[1]/this.host._decorH-r/2),w:n,h:r,opacity:1}],this.host._decorSel=l,this.host._decorTool="select",this.host._decorImagePalette=null,this.hooks.clearFurniturePreview(),this.hooks.recordGeometry(this.host._t("history.decor_add"),h),this.hooks.saveConfig(),this.host.requestUpdate()}renderPlacementPreview(){const t=this.host._furnPreviewInput,e=this.host._decorImagePalette;if(!t||!e||"decor"!==this.host._mode||"image"!==this.host._decorTool||!this.host._pointerModality.hoverEnabled)return d;const s=this.hooks.decorSnap(t.raw,"mouse"),i=It(e.width,e.height),o=Tt(i.w,this.host._cellCm,this.host._gridPitch)/E,a=Tt(i.h,this.host._cellCm,this.host._gridPitch)/this.host._decorH,n=this.host._display(e.url);return n?Rt``:d}renderMissing(t,e,s,i,o,a,n,r,l){const h=i+a/2,c=o+n/2;return Rt` - ${this.host._t("decor.image_unavailable")} - - - ${this.host._t("decor.image_unavailable")} - `}async uploadFromInput(t,e=!1){const s=t.target,i=s.files?.[0];if(s.value="",!i||this.host._decorAssetBusy)return;let o;try{o=await so(i,2097152)}catch{return void this.host._showToast(this.host._t("backup.error.invalid_image"))}if("reject"!==o.kind)return"guard"===o.kind?(this.hooks.setGuardReplace(e),void(this.host._backdropGuard=o.state)):void await this.upload(i,i.name,e);this.host._showToast(this.host._t("toast.plan_formats"))}async upload(t,e,s){if(!this.host._decorAssetBusy){this.host._decorAssetBusy=!0;try{const i=new FormData;i.append("file",t,e);const o=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/assets/upload",{method:"POST",body:i}):await fetch("/api/houseplan/assets/upload",{method:"POST",body:i,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await o.json().catch(()=>({}));if(!o.ok||a.error||!a.asset){const t={invalid_format:"backup.error.invalid_image",invalid_image:"backup.error.invalid_image",unsupported_image:"backup.error.unsupported_image",too_large:"backdrop.too_large_title",capacity_exceeded:"decor.image_error_capacity"};throw new Error(this.host._t(t[String(a.error)]||"backup.error.io_error"))}const n=zt({assets:[a.asset]}).values().next().value;if(!n)throw new Error(this.host._t("backup.error.io_error"));this.host._decorAssets=new Map(this.host._decorAssets).set(n.asset_id,n),this.host._decorAssetCatalog=[n,...this.host._decorAssetCatalog.filter(t=>t.asset_id!==n.asset_id)],s&&"image"===this.host._decorShapeDialog?.kind?this.host._decorShapeDialog={...this.host._decorShapeDialog,assetId:n.asset_id}:this.host._decorImagePalette=n,this.host.requestUpdate()}catch(t){this.host._showToast(this.host._t("decor.image_upload_failed",{err:this.host._errText(t)}))}finally{this.host._decorAssetBusy=!1}}}async delete(t){if(t.used_by?.length)return void this.host._showToast(this.host._t("decor.image_in_use"));if(await this.host._confirmDanger({key:`delete-decor-asset:${t.asset_id}`,kind:"destructive",title:this.host._t("decor.image_delete_title"),message:this.host._t("decor.image_delete_message",{name:t.name}),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}))try{await this.host.hass.callWS({type:"houseplan/assets/delete",asset_id:t.asset_id}),this.host._decorAssetCatalog=this.host._decorAssetCatalog.filter(e=>e.asset_id!==t.asset_id),this.host._decorAssets.delete(t.asset_id),this.host._decorImagePalette?.asset_id===t.asset_id&&(this.host._decorImagePalette=null),this.host.requestUpdate()}catch(t){this.host._showToast("in_use"===t?.code?this.host._t("decor.image_in_use"):this.host._t("backup.error.io_error"))}}async loadCatalog(){try{const t=await this.host.hass.callWS({type:"houseplan/assets/list"}),e=zt(t);this.host._decorAssetCatalog=[...e.values()],this.host._decorAssets=new Map([...this.host._decorAssets,...e]),this.host.requestUpdate()}catch{this.host._showToast(this.host._t("backup.error.io_error"))}}renderImagePalette(){const t=this.host._decorImagePalette;return p``}renderFurniturePalette(){const t=this.host._furnPalette,e=this.host._furnCategory,s=At.flatMap(t=>Ft(t)),i=Ll.filter(t=>s.some(e=>e.category===t.id)),o=i.find(t=>t.id===e)||null,a=o?s.filter(t=>t.category===o.id):[],n=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`
    t.stopPropagation()}> -
    - ${this.host._t("furn.title")} - - -
    -
    - ${o?p` - -
    - ${this.host._t(`furn.cat_${o.id}`)} -
    -
    - ${a.map(e=>p``)} -
    - `:At.map(t=>p` -
    ${this.host._t(`furn.group_${t}`)}
    -
    - ${i.filter(e=>e.group===t).map(t=>p``)} -
    `)} -
    - ${t?p`
    - - this.host._furnPalette={...t,w:this.hooks.furnFieldToCm(Number(e.target.value))}} /> - - this.host._furnPalette={...t,h:this.hooks.furnFieldToCm(Number(e.target.value))}} /> - ${this.host._t("furn.place_hint")} -
    `:p`
    ${this.host._t("furn.pick_hint")}
    `} -
    `}furnitureSizeInput(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i=Nt(e,this.host._imperial,Ht*this.host._cellCm),o="w"===t?"sizeWField":"sizeHField",a="w"===t?"sizeWCm":"sizeHCm",n="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[o]:e,...null===i?{}:{[a]:Math.abs(i),[n]:i<0}}}furnitureFlip(t,e){const s=this.host._decorShapeDialog;if(!s||"furniture"!==s.kind&&"image"!==s.kind)return;const i="w"===t?s.sizeWField:s.sizeHField,o=Nt(i,this.host._imperial,Ht*this.host._cellCm),a="w"===t?s.sizeWCm:s.sizeHCm,n=Math.abs(o??a??.1),r="w"===t?"sizeWField":"sizeHField",l="w"===t?"flipH":"flipV";this.host._decorShapeDialog={...s,[r]:Lt(n,e,this.host._imperial),[l]:e}}renderShapeDialog(){const t=this.host._decorShapeDialog,e="rect"===t.kind||"ellipse"===t.kind,s=this.host._t(`decor.${t.kind}`),i=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m"),o="furniture"===t.kind||"image"===t.kind?Nt(t.sizeWField,this.host._imperial,Ht*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?Nt(t.sizeHField,this.host._imperial,Ht*this.host._cellCm):null,n=!("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a),r="image"===t.kind?this.host._decorAssetCatalog.find(e=>e.asset_id===t.assetId)||this.host._decorAssets.get(t.assetId||""):null;return p`this.host._decorShapeDialog=null}> -
    - ${"furniture"===t.kind?p` - - `:"image"===t.kind?p` - - - ${r?p`
    - ${r.name} - ${r.name} -
    `:d} - `:d} - ${"image"===t.kind?p` - - this.host._decorShapeDialog={...t,opacity:Number(e.target.value)}} />`:p`this.host._decorShapeDialog={...t,...e.detail}}>`} - ${"image"!==t.kind?p` -
    this.host._decorShapeDialog={...t,widthCm:this.host._decorSmallCm(Number(e.target.value))}} />${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    `:d} - ${"line"===t.kind?p` - -
    - - -
    - -
    this.host._decorShapeDialog={...t,lengthCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ${i}
    `:"furniture"===t.kind||"image"===t.kind?p` - -
    this.furnitureSizeInput("w",t.target.value)} /> - ×this.furnitureSizeInput("h",t.target.value)} /> - ${i}
    - - `:p` - -
    this.host._decorShapeDialog={...t,sizeWCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ×this.host._decorShapeDialog={...t,sizeHCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ${i}
    `} - - this.host._decorShapeDialog={...t,angle:e.target.value}} /> - ${e?p` - this.host._decorShapeDialog={...t,fillColor:e.detail.color,fillOpacity:e.detail.opacity}}>`:d} -
    -
    - - - -
    -
    `}}const Ol=(t,e)=>t[0]*e[1]-t[1]*e[0],Bl=(t,e)=>t[0]*e[0]+t[1]*e[1],Wl=(t,e)=>[t[0]-e[0],t[1]-e[1]],jl=(t,e,s)=>[t[0]+e[0]*s,t[1]+e[1]*s];function Gl(t,e,s){return Math.abs(Ol(t,e))<=s}function ql(t,e,s){return Math.abs(Ol(Wl(t,e),s))}function Ul(t,e,s,i,o){const a=e[0]-t[0],n=e[1]-t[1],r=Math.hypot(a,n);if(!(r>o&&s>0))return[];const l=[a/r,n/r];return function(t,e,s){const i=e.map(([e,s])=>[Math.max(0,Math.min(e,s)),Math.min(t,Math.max(e,s))]).filter(([t,e])=>e>t+s).sort((t,e)=>t[0]-e[0]||t[1]-e[1]),o=[];for(const t of i){const e=o[o.length-1];!e||t[0]>e[1]+s?o.push([...t]):e[1]=Math.max(e[1],t[1])}const a=[];let n=0;for(const[t,e]of o)t>n+s&&a.push([n,t]),n=Math.max(n,e);return t>n+s&&a.push([n,t]),a}(r,i.flatMap(e=>{const i=e.b[0]-e.a[0],a=e.b[1]-e.a[1],n=Math.hypot(i,a);if(!(n>o&&Gl(l,[i/n,a/n],1e-6)))return[];if(ql(e.a,t,l)>1.1*Math.max(s,o)||ql(e.b,t,l)>1.1*Math.max(s,o))return[];return[[Bl(Wl(e.a,t),l),Bl(Wl(e.b,t),l)]]}),o).flatMap(([e,i])=>{const o=Bt({a:jl(t,l,e),b:jl(t,l,i),halfDepth:s});return o?[o]:[]})}function Kl(t){const e=t.coordScale??1,s=Math.max(t.epsilon??2e-4*t.gridPitch,1e-9),i=[];for(const s of t.rooms||[]){if(!s?.id)continue;const o=H(t.rooms,s.id,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e);if(!o||o.poly.length<3)continue;const a=Et(o.poly,o.offsets);!a||a.length<3||i.push({roomId:s.id,profile:o,inner:a})}i.sort((t,e)=>t.roomId.localeCompare(e.roomId));const o=(t.roomOpenings||[]).map(t=>{const e=t.angle*Math.PI/180,s=[Math.cos(e),Math.sin(e)],i=Math.max(0,t.length)/2;return{a:jl([t.x,t.y],s,-i),b:jl([t.x,t.y],s,i)}}),a=[];for(const i of Ot(t.rooms,t.walls,t.openCuts,t.pitch,t.cellCm,t.gridPitch,e))for(const t of Ul(i.a,i.b,i.depthUnits/2,o,s))a.push({body:t});const n=new Map;for(const e of t.partitionCuts||[]){const t=n.get(e.hostId)||[];t.push(e),n.set(e.hostId,t)}for(const e of t.partitions||[]){const i=nt(e.cm,t.cellCm,t.gridPitch)/2;for(const t of Ul(e.a,e.b,i,n.get(e.id)||[],s))a.push({body:t,partitionId:e.id})}return{rooms:i,boundaries:a,epsilon:s}}function Vl(t,e,s,i){const o=t.profile.offsets[e];if(!(o>0))return null;const a=function(t,e){const s=t[e],i=t[(e+1)%t.length],o=i[0]-s[0],a=i[1]-s[1],n=Math.hypot(o,a)||1;return function(t){let e=0;for(let s=0;s=0?[-a/n,o/n]:[a/n,-o/n]}(t.profile.poly,e),n=jl(s.center,a,o),r=[];for(let e=0;ei&&Gl(s.axis,[l[0]/h,l[1]/h],1e-6)))continue;if(ql(o,n,s.axis)>i||ql(a,n,s.axis)>i)continue;const c=Bl(Wl(o,n),s.axis),d=Bl(Wl(a,n),s.axis);r.push([Math.min(c,d),Math.max(c,d)])}const l=function(t,e){t.sort((t,e)=>t[0]-e[0]||t[1]-e[1]);const s=[];for(const i of t){const t=s[s.length-1];!t||i[0]>t[1]+e?s.push([...i]):t[1]=Math.max(t[1],i[1])}return s}(r,i).find(([t,e])=>t<=i&&e>=-i);if(!l)return null;const[h,c]=l,d=jl(n,s.axis,-s.half),p=jl(n,s.axis,s.half),u=Math.max(0,-s.half-h),_=Math.max(0,c-s.half),m=u>0?jl(n,s.axis,h):d,g=_>0?jl(n,s.axis,c):p,f=Bl(a,s.normal)>=0?1:-1,b=(e,i,o)=>({from:e,to:i,label:[(e[0]+i[0])/2,(e[1]+i[1])/2],axis:s.axis,labelNormal:a,distance:o,roomId:t.roomId,roomSide:f,source:"room-face"});return{left:b(d,m,u),right:b(p,g,_)}}function Jl(t){const e=jl(t.center,t.axis,-t.half),s=jl(t.center,t.axis,t.half),i=jl(t.center,t.axis,Math.min(-t.half,t.targetLo)),o=jl(t.center,t.axis,Math.max(t.half,t.targetHi)),a=(e,s)=>({from:e,to:s,label:[(e[0]+s[0])/2,(e[1]+s[1])/2],axis:t.axis,labelNormal:t.normal,distance:Math.hypot(s[0]-e[0],s[1]-e[1]),source:"host-end"});return[a(e,i),a(s,o)]}function Yl(t,e,s,i){const o=Wl(s,e),a=Bl(o,o);if(!(a>i*i))return Math.hypot(...Wl(t,e))<=i;const n=Bl(Wl(t,e),o)/a;if(n<-i||n>1+i)return!1;const r=jl(e,o,Math.max(0,Math.min(1,n)));return Math.hypot(...Wl(t,r))<=i}function Zl(t,e,s){let i=!1;for(let o=0,a=e.length-1;ot[1]!=h>t[1]&&t[0]<(l-n)*(t[1]-r)/(h-r)+n&&(i=!i)}return i}function Xl(t,e,s,i,o,a){const n=Wl(i,s),r=Ol(e,n);if(Math.abs(r)<=a)return null;const l=Wl(s,t),h=Ol(l,n)/r,c=Ol(l,e)/r;return h<-a||h>o+a||c<-a||c>1+a?null:Math.max(0,Math.min(o,h))}function Ql(t,e,s){const i=Jl(s),o="partition"===t.host?.kind?t.host.id:void 0;return[{base:i[0],direction:[-s.axis[0],-s.axis[1]]},{base:i[1],direction:s.axis}].map(({base:t,direction:s})=>{const i=t.distance,a=function(t,e,s,i,o,a){let n=null;for(const r of i)if(!o||r.partitionId!==o){if(Zl(t,r.body,a))return 0;for(let i=0;iBl(Wl(t,o),s));return{center:o,axis:s,normal:i,half:Math.max(0,t.renderedLength)/2,targetLo:Math.min(...a),targetHi:Math.max(...a)}}(t),i=e.rooms.flatMap(t=>{const i=function(t,e,s){const{profile:i}=t;let o=null;for(let t=0;t0))continue;const a=i.poly[t],n=i.poly[(t+1)%i.poly.length],r=Wl(n,a),l=Math.hypot(r[0],r[1]);if(!(l>s&&Gl(e.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(ql(a,e.center,e.axis)>s||ql(n,e.center,e.axis)>s)continue;const h=Bl(Wl(a,e.center),e.axis),c=Bl(Wl(n,e.center),e.axis),d=Math.min(h,c),p=Math.max(h,c);if(d>-e.half+s||p0?d:p<0?-p:0);(!o||u2)return Jl(s);if(i.length){const t=i.map(({room:t,edge:i})=>Vl(t,i,s,e.epsilon));if(t.some(t=>!t))return Jl(s);const o=t;return[...o.map(t=>t.left),...o.map(t=>t.right)]}return Ql(t,e,s)}const eh="1.72.0-beta.2",sh={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},ih={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},oh=2e3,ah=1e3,nh=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const s=Number(e);return Number.isFinite(s)?s:null},rh=(t,e,s,i)=>{for(t.delete(e),t.set(e,s);t.size>i;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}},lh=1e3,hh=2e3,ch=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}},dh="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";class ph{constructor(t){this.host=t,this._junctionBaselineCache=new WeakMap,this._supportPreviewGeneration=0,this._decorAssetGuardReplace=null,this._undoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.undo();t&&(this._applyGeometryState(t.before,!0)?this.host._showToast(this.host._t("history.undone",{name:t.name})):this.host._geometryHistory.clear())},this._redoGeometry=()=>{if(this.host._physicalDrag||this.host._physicalRotate)return void this._cancelPhysicalGesture();if(this.host._decorDraft)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)return void this._cancelDecorGesture();if(this.host._resize.dragging)return void this._rszCancelDrag();const t=this.host._geometryHistory.redo();t&&(this._applyGeometryState(t.after,!0)?this.host._showToast(this.host._t("history.redone",{name:t.name})):this.host._geometryHistory.clear())},this._savePhysicalDialog=()=>{const t=this.host._physicalDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=nh(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Wt:100,"column"===t.kind?1:0);const o=this.host._imperial?2.54*i:i,a="column"===t.kind?Wt:100,n="column"===t.kind?1:0;if(!Number.isFinite(o)||oa)return void this._showPhysicalRange(a,n);if("column"===t.kind){const e=s.wall_columns.find(e=>e.id===t.id);if(!e)return;const i=nh(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this.host._showToast(this.host._t("toast.physical_angle"));const a="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:o}:{id:t.id,shape:"square",center:e.center,cm:o,angle:i};if(s.wall_columns.some(e=>e.id!==t.id&&jt(e,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===t.kind){const s=(e.partitions||[]).find(e=>e.id===t.id);if(s&&0===o&&Gt(e.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));s&&(s.cm=o)}else if("column"===t.kind){const s=(e.wall_columns||[]).find(e=>e.id===t.id);s&&(s.cm=qt(o),s.shape="circle"===t.shape?"circle":"square","square"===s.shape?s.angle=nh(t.angle||"0"):delete s.angle)}else{const s=(e.room_drafts||[]).find(e=>e.id===t.id);s?.segments?.[t.segment||0]&&(s.segments[t.segment||0].cm=o)}const l=this._commitPhysicalGeometry(this.host._t("history.physical_edit"),r);this.host._physicalDialog=null,l||this.host.requestUpdate()},this._deletePhysicalSelection=async()=>{const t=this.host._physicalSel,e=this.host._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void await this._deleteDraftWhole();const s=this._geometrySnapshot();if("partition"===t.kind){const s=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===t.id).sort((t,e)=>(t.host?.t||0)-(e.host?.t||0));if(s.length)return void(this.host._partitionDeleteDialog={id:t.id,openings:s.map(t=>JSON.parse(JSON.stringify(t)))})}const i="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[i]=(e[i]||[]).filter(e=>e.id!==t.id),e[i].length||delete e[i],this.host._activeDraftId===t.id&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._confirmPartitionDelete=()=>{const t=this.host._partitionDeleteDialog,e=this.host._curSpaceCfg;if(!t||!e)return;const s=this._geometrySnapshot();e.partitions=(e.partitions||[]).filter(e=>e.id!==t.id),e.partitions.length||delete e.partitions,e.openings=(e.openings||[]).filter(e=>"partition"!==e.host?.kind||e.host.id!==t.id),e.openings.length||delete e.openings,this.host._partitionDeleteDialog=null,this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),s)},this._deleteDraftWhole=async()=>{const t="draft"===this.host._physicalDialog?.kind?this.host._physicalDialog.id:"draft"===this.host._physicalSel?.kind?this.host._physicalSel.id:null,e=this.host._space;if(!t||!this.host._curSpaceCfg)return;if(!await this.host._confirmDanger({key:"delete-draft",kind:"destructive",title:this.host._t("confirm.delete_draft_title"),message:this.host._t("confirm.delete_draft_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")})||this.host._space!==e)return;const s=this.host._curSpaceCfg;if(!s||!(s.room_drafts||[]).some(e=>e.id===t))return;const i=this._geometrySnapshot();s.room_drafts=(s.room_drafts||[]).filter(e=>e.id!==t),s.room_drafts.length||delete s.room_drafts,this.host._activeDraftId===t&&this._cancelPath(),this.host._physicalSel=null,this.host._physicalDialog=null,this._commitPhysicalGeometry(this.host._t("history.physical_delete"),i)},this._deleteDraftSegment=async()=>{const t=this.host._physicalDialog,e=this.host._space;if(!t||"draft"!==t.kind||!this.host._curSpaceCfg)return;const s=t.id,i=t.segment||0,o=await this.host._confirmDanger({key:"delete-draft-segment",kind:"destructive",title:this.host._t("confirm.delete_draft_segment_title"),message:this.host._t("confirm.delete_draft_segment_body"),confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._physicalDialog;if(!o||this.host._space!==e||"draft"!==a?.kind||a.id!==s||(a.segment||0)!==i)return;const n=this.host._curSpaceCfg;if(!n)return;const r=(n.room_drafts||[]).findIndex(t=>t.id===s);if(r<0)return;const l=n.room_drafts[r];if(!Array.isArray(l.segments)||!Number.isInteger(i)||i<0||i>=l.segments.length)return;const h=i,c=[],d=l.points.slice(0,h+1),p=l.points.slice(h+1);if(d.length>=2&&c.push({id:l.id,points:d,segments:l.segments.slice(0,h)}),p.length>=2&&c.push({id:c.length?`${l.id}-${Date.now().toString(36)}`:l.id,points:p,segments:l.segments.slice(h+1)}),2===c.length&&n.room_drafts.length>=200)return void this.host._showToast(this.host._t("toast.physical_limit"));const u=this._geometrySnapshot();n.room_drafts.splice(r,1,...c),n.room_drafts.length||delete n.room_drafts,this.host._activeDraftId===l.id&&this._cancelPath(),this.host._physicalDialog=null,this.host._physicalSel=c.length?{kind:"draft",id:c[0].id}:null,this._commitPhysicalGeometry(this.host._t("history.draft_segment_delete"),u)},this._furnShiftListener=t=>{if("Shift"!==t.key)return;const e=this.host._furnPreviewInput;if(!e)return;const s="keydown"===t.type;e.free!==s&&(this.host._furnPreviewInput={...e,free:s},this.host.requestUpdate())},this._furnShiftAttached=!1,this._confirmRoomDelete=t=>{const e=this.host._roomDeleteDialog,s=this.host._curSpaceCfg,i=this.host._spaceModel();if(!e||!s||!i)return;const o=i.rooms.find(t=>t.id===e.roomId);if(!o)return void(this.host._roomDeleteDialog=null);const a=this.host._openCuts(),n=Zn(Z(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh).filter(t=>t.roomId===o.id),i.partitions,this.host._openingsR.map(t=>({...t,x:t.rx,y:t.ry})),.02*this.host._gridPitch),r=new Set(n.materialize.filter(t=>!t.reusePartitionId).map(t=>t.interval.key)).size;if(t&&(s.partitions||[]).length+r>hh)return void this.host._showToast(this.host._t("toast.physical_limit"));const l=this._geometrySnapshot(),h=Ut(i.rooms,this.host._spaceWalls,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh);if(t){s.partitions||=[];const t=new Map,e=new Map,i=t=>{const e=(s.wall_segments||[]).find(e=>{const s=[Number(e.a?.[0])*lh,Number(e.a?.[1])*lh],i=[Number(e.b?.[0])*lh,Number(e.b?.[1])*lh];return this._samePt(t.a,s)&&this._samePt(t.b,i)||this._samePt(t.a,i)&&this._samePt(t.b,s)});return"string"==typeof e?.id&&e.id?e.id:null},o=Date.now().toString(36);for(const a of n.materialize){let n=a.reusePartitionId?s.partitions.find(t=>t.id===a.reusePartitionId):e.get(a.interval.key);n||(n={id:i(a.interval)||`partition-room-${o}-${e.size}`,a:[a.interval.a[0]/lh,a.interval.a[1]/lh],b:[a.interval.b[0]/lh,a.interval.b[1]/lh],cm:a.interval.cm},s.partitions.push(n),e.set(a.interval.key,n)),t.set(a.interval.key,n)}for(const e of s.openings||[]){const s=n.openingIntervals.get(e.id),i=s?t.get(s):null,o=this.host._openingsR.find(t=>t.id===e.id);if(!i||!o)continue;const a={a:[i.a[0]*lh,i.a[1]*lh],b:[i.b[0]*lh,i.b[1]*lh]};e.host={kind:"partition",id:i.id,t:Xn([o.rx,o.ry],a)}}}else if(n.removeOpeningIds.length){const t=new Set(n.removeOpeningIds);s.openings=(s.openings||[]).filter(e=>!t.has(e.id)),s.openings.length||delete s.openings}s.rooms=s.rooms.filter(t=>t.id!==o.id),this.host._cfgEpoch++;const c=this.host._normalizeWalls(h,this.host._openCuts());c.length?s.walls=c:delete s.walls,this.host._roomDeleteDialog=null,this._commitPhysicalGeometry(this.host._t(t?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()},this._rebindPartitionOpening=()=>{const t=this.host._openingDialog;if(!t?.id)return;const e=t.id;this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingRebindId=e,this.host._openingPreset={type:t.type,lengthCm:t.lengthCm,flipH:t.flipH,flipV:t.flipV,revision:++this.host._openingPresetRevision},this.host._openingDialog=null,this.host._openingHoverCandidate=null)},this._deviceInboxTabKey=t=>{if("ArrowLeft"!==t.key&&"ArrowRight"!==t.key)return;const e=this.host._deviceInbox;if(!e)return;const s=["on_plan","available","hidden","readd"],i="ArrowRight"===t.key?1:-1,o=(s.indexOf(e.tab)+i+s.length)%s.length;this.host._deviceInbox={...e,tab:s[o],limit:100,onlyNew:!1},t.preventDefault()},this._keepClosedAsPartitions=()=>{if(this.host._wallFaceBatch)return void this._decideWallFace(!1);if(!this.host._contourClosed||this.host._pendingSplit||!this.host._curSpaceCfg)return;const t=this.host._curSpaceCfg,e=this.host._path.slice(0,-1);if((t.partitions||[]).length+e.length>hh)return void this.host._showToast(this.host._t("toast.physical_limit"));const s=this._geometrySnapshot(),i=xn(e.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,X);t.partitions||=[];const o=Date.now().toString(36),a=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,n=this._draftSegmentsForPath(this.host._path,a,i);for(let s=0;st.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.contour_to_partitions"),s),this.host._roomDialog=!1,this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}},this._openSettingsDialog=()=>{if(!this.host._norm)return;const t=this.host._glowRadiusCm,e=this.host._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this.host._settingsDialog={colors:JSON.parse(JSON.stringify(this.host._fillColors)),glowRadius:e,bgColor:Xt(this.host._settings,{bgColor:null})||null,northDeg:Zt(this.host._settings,{}),bgMode:Yt(this.host._settings,{}),sunRays:Jt(this.host._settings,{}),showRoomTooltip:Vt(this.host._settings),zigbeeTopology:Kt(this.host._settings),busy:!1}},this._openSupportDialog=()=>{this.host._norm&&this.host._canEdit&&(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPreviewGeneration+=1,this.host._supportDialog={draftId:Tl("draft"),idempotencyKey:Tl("report"),submissionFingerprint:"",contact:"",message:"",attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:"",reportId:""})},this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const t=this.host._alignDialog;t&&!t.busy&&t.report.liveMissingPositions.length&&this._previewAlignDialog(!t.removeLiveMissingPositions)},this._openBackupExport=()=>{this.host._settingsDialog=null,this.host._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._decorStylePersistTimer=null,this._openRulesDialog=()=>{if(!this.host._norm)return;const t=this.host._settings.icon_rules,e=(t&&t.length?t:Qt).map(t=>({...t}));this.host._rulesDialog={rules:e,test:"",busy:!1}},this._discoveryPreviewMemo=null,this._decorImages=new El(t,{decorSnap:(t,e)=>this._decorSnap(t,e),geometrySnapshot:()=>this._geometrySnapshot(),clearFurniturePreview:()=>this._clearFurniturePreview(),recordGeometry:(t,e)=>this._recordGeometry(t,e),saveConfig:()=>this._saveConfig(),saveShape:()=>this._decorSaveShape(),setGuardReplace:t=>{this._decorAssetGuardReplace=t},furnShiftDetach:()=>this._furnShiftDetach(),furnPick:t=>this._furnPick(t),furnFieldValue:t=>this._furnFieldValue(t),furnFieldToCm:t=>this._furnFieldToCm(t)}),t._editorSecondary=new rr({root:()=>t.renderRoot,requestUpdate:()=>t.requestUpdate(),updateComplete:()=>t.updateComplete,clearTip:()=>{t._tip=null}}),t._resize=new Da}_help(t){const e=`${t}.aria`,s=l(this.host.hass,this.host._config?.language);return te(s,t)&&te(s,e)?p``:d}_setMode(t,e=!0){t!==this.host._mode&&this.host._cancelDangerConfirm(),this.host._endTabDrag(),this._clearFurniturePreview(),this.host._pendingNavMode=null;const s=this.host._spaceModel();if(!s)return this.host._cancelModeTransition(!1),void(this.host._mode="view");if(this.host._kiosk&&"view"!==t)return;if(this.host._mode===t){if(!e&&"view"===t&&this.host._modeTransitionBusy){const e=this.host._currentModeVisual(t),s=this.host._modeTransitionTargetZoom,i=this.host._modeTransitionTargetCenterX,o=this.host._modeTransitionTargetCenterY;this.host._cancelModeTransition(!1),this.host._commitViewModeAtomic(e,s,i,o)}return}if("devices"===this.host._mode&&this.host._cancelDeviceDrag(),this.host._bootSoftCancel(),("plan"===t||"decor"===t)&&!this.host._norm)return void this.host._showToast(this.host._t("toast.markup_needs_server"));if(this.host._wallFaceBatch&&this._roomDialogCancel(),"plan"===this.host._mode&&"draw"===this.host._tool&&!this._finishWallChain())return;this.host._clearTransientHover(!0),this.host._cancelDevicePressFeedback();const i=this.host._mode,o=this.host._effectiveProjection(),a=this.host._currentModeVisual(i),n=this.host._modeTransitionBusy,r=a&&"view"!==i&&"view"!==t?{...a,toolbarContentOpacity:this.host._reducedMotion?1:.35}:a;this.host._cancelModeTransition(!1),this.host._editorSecondary.closeForNavigation(),(this.host._decorMove||this.host._dtDrag||this.host._bdDrag)&&this._cancelDecorGesture();const l=!s.bg&&"view"===t!=("view"===i),h=n&&"view"===i&&"view"!==t?this.host._modeTransitionEditorCamera:null;let c=h?.zoom??(n?this.host._modeTransitionTargetZoom:this.host._zoom),d=h?h.centerX:n?this.host._modeTransitionTargetCenterX:r?.viewport.centerX,p=h?h.centerY:n?this.host._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===i&&"view"!==t&&!n){const e=this.host._view;if(this.host._viewModeSnap={space:this.host._space,zoom:this.host._zoom,cx:e?this.host._logicalViewCenter(o)?.x:void 0,cy:e?this.host._logicalViewCenter(o)?.y:void 0},"iso"===o){const e=this.host._logicalViewCenter("iso");d=e?.x,p=e?.y,this.host._view=null,this.host._mode=t,this.host._applyView(this.host._zoom,e?.x,e?.y)}l&&(c=1,d=void 0,p=void 0)}if("plan"===i&&this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._mode=t,"devices"===i&&"devices"!==t&&(this.host._showHidden=!1,this.host._deviceInbox=null,this.host._deviceInboxReturn=null,this.host._deviceInboxMemo=null),this.host._editorChromeMode="view"===t?i:t,"view"===t){"view"===i||n||(this.host._modeTransitionEditorCamera={zoom:this.host._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const t=this.host._viewModeSnap;if(t&&t.space===this.host._space){c=t.zoom;const e=this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space]?"iso":"flat",s=null!=t.cx&&null!=t.cy&&"iso"===e?ee([t.cx,t.cy],0):null;d=s?.[0]??t.cx,p=s?.[1]??t.cy}else t&&(c=this.host._zoomBySpace[this.host._space]||1,d=void 0,p=void 0)}else this.host._modeTransitionEditorCamera={zoom:c,centerX:d,centerY:p};this.host._modeTransitionTargetZoom=c,this.host._modeTransitionTargetCenterX=d,this.host._modeTransitionTargetCenterY=p,("iso"===o||"view"===t&&this.host._labsIso&&"iso"===this.host._viewPreference[this.host._space])&&(this.host._modeTransitionForceAtomic=!0);const u=++this.host._modeTransitionRequest;e?r?(this.host._modeTransitionPreparing=!0,this.host._modeTransitionVisual=r,this.host._prepareModeTransition(u,r,t,c,d,p)):(this.host._modeTransitionPreparing=!1,this.host._modeTransitionVisual=null,this.host._zoom=c,this.host._view=null,requestAnimationFrame(()=>{this.host.isConnected&&u===this.host._modeTransitionRequest&&this.host._mode===t&&(this.host._applyView(c,d,p),"view"===t&&(this.host._viewModeSnap=null,this.host._modeTransitionEditorCamera=null),this.host.requestUpdate())})):"view"===t&&this.host._commitViewModeAtomic(r,c,d,p),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this.host._tool="draw",this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._selId=null,this.host._physicalSel=null,this.host._physicalDialog=null,this.host._physicalDrag=null,this._rszResetController(),this.host._tip=null,this.host._hoverRoom=null,this.host._decorDraft=null,this.host._decorSel=null,this.host._decorMove=null,this.host._backdropDialog=null,this.host._decorTool="select",this.host._bdDrag=null,this.host._dtDrag=null,this.host._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this.host._saveNav()}_primeDrawWallField(){null===this.host._drawWallField&&(this.host._drawWallField=se(X,this.host._imperial))}_showPhysicalRange(t=this.host._drawWallMaxCm,e=0){this.host._showToast(this.host._t("toast.physical_range",{min:se(e,this.host._imperial),max:se(t,this.host._imperial),unit:this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this.host._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_mergeSpacePartitions(t,e){const s=t?.partitions||[];if(s.length<2)return 0;const i=kr(s,{pitch:U,seedIds:e,geometry:Sr(t,{excludeDraftId:this.host._activeDraftId})});return i.merged?(t.partitions=i.partitions,xr(t.openings,t.partitions,i.openingMoves,{coordScale:lh,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch}),i.merged):0}_finishWallChain(){if("draw"!==this.host._tool||this.host._wallFaceBatch||this.host._roomDialog)return!0;const t=this.host._curSpaceCfg;if(!t||this.host._path.length<2)return this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0;const e=Sn(this.host._path,xn(this.host._path.length-1,this.host._draftSegmentCms,this.host._drawWallCm,X)),s=e.length;if((t.partitions||[]).length+s>hh)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const i=this._geometrySnapshot();t.partitions||=[];const o=Date.now().toString(36),a=[],n=this.host._activeDraftId?(t.room_drafts||[]).find(t=>t.id===this.host._activeDraftId):null,r=this._draftSegmentsForPath(this.host._path,n,e.map(t=>t.cm));for(let i=0;it.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._commitPhysicalGeometry(this.host._t("history.wall_chain_finish"),i),delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(t){t!==this.host._tool&&(this.host._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this.host._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this.host._tool&&(this.host._resize.dragging?this._rszCancelDrag():this.host._resize.reset()),this.host._tool=t,"draw"===t&&this._resumeLastDraft(),"resize"===t&&this.host._resize.selectRoom(null),"wallthick"===t&&(this.host._wallDialog=null)))}_limitReached(t){const e=this.host._curSpaceCfg;if(!e)return!0;const s="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=hh:(e.wall_columns||[]).length>=500;return s&&this.host._showToast(this.host._t("toast.physical_limit")),s}_svgPoint(t){const e=this.host.renderRoot.querySelector(".stage").getBoundingClientRect();return this.host._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this.host._gridPitch;return[ie(oe(t[0],e)),ie(oe(t[1],e))]}_snapDrawPoint(t,e=!1){const s=this.host._path[this.host._path.length-1],i=e&&s?ae(s,t,this.host._gridPitch,Xi):t;return this._snap(i)}_planSnapOpeningCuts(t,e){if(!this.host._openingsR.length)return[];const s=this.host._openingWallIndexFor(t,e).value,i=[];for(const e of this.host._roomWallOpeningInputs(this.host._openingsR,t)){const t=at(s,e);if(!t.negative&&!t.positive)continue;const o=e.angle*Math.PI/180,a=Math.cos(o)*e.length/2,n=Math.sin(o)*e.length/2;i.push([e.x-a,e.y-n,e.x+a,e.y+n])}return i}_planSnapGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|empty`,value:{segments:[],endpoints:[]}};const e=[this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planSnapGeometryCache?.key===e)return this.host._planSnapGeometryCache;const s=this.host._openCuts(),i=gn({space:t,activeDraftId:this.host._activeDraftId,roomCuts:this._planSnapOpeningCuts(t,s),partitionCuts:this._partitionOpeningCuts(t),epsilon:2e-4*this.host._gridPitch});return this.host._planSnapGeometryCache={key:e,value:i},this.host._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const e=["hidden",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._hiddenWallDiagnosticCache?.key===e)return this.host._hiddenWallDiagnosticCache;const s=mn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._hiddenWallDiagnosticCache={key:e,value:s},this.host._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const t=this.host._spaceModel();if(!t)return{key:`${this.host._space}|structural-empty`,value:{segments:[],endpoints:[]}};const e=["structural",this.host._space,this.host._cfgEpoch,this.host._activeDraftId||"",t.rooms.length,t.room_drafts.length,t.partitions.length].join("|");if(this.host._planStructuralGeometryCache?.key===e)return this.host._planStructuralGeometryCache;const s=gn({space:t,activeDraftId:this.host._activeDraftId,epsilon:2e-4*this.host._gridPitch});return this.host._planStructuralGeometryCache={key:e,value:s},this.host._planStructuralGeometryCache}_planSnapContextKey(t){const e=this.host._path[0],s=this.host._path[this.host._path.length-1];return[t,this.host._tool,this.host._path.length,e?`${e[0]},${e[1]}`:"",s?`${s[0]},${s[1]}`:""].join("|")}_resolvePlanDrawPoint(t,e){const s=this._planSnapGeometrySnapshot(),i=this.host._path[this.host._path.length-1],o="draw"===this.host._tool&&this.host._path.length>=3?[{point:this.host._path[0],key:"closure:first-point"}]:[],a={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this.host._gridPitch,excludePoints:i?[i]:[],extraEndpoints:o,epsilon:2e-4*this.host._gridPitch},n=e&&i?wn(s.value,t,{...a,anchor:i}):function(t,e,s){if(!(rn(e)&&s.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const i=s.epsilon??nn,o=s.excludePoints||[],a=yn(vn(t,e,s),s.distinguishTolerance||0);if(a)return a;let n=null;for(const a of t.segments){const t=[a.a[0],a.a[1],a.b[0],a.b[1]],r=G([e[0],e[1]],t);if(r>s.tolerance)continue;const l=$n(a,e,s.gridStep);!fn(l,o,i)&&bn(r,a.key,n)&&(n={kind:"line",point:l,key:a.key,distance:r,segment:a})}return n?{kind:"resolved",candidate:n,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(s.value,t,a),r="resolved"===n.kind?n.candidate:null,l=r?[...r.point]:this._snapDrawPoint(t,e),h=i?ne(i,l):l;return{point:h,candidate:r&&rt(h,r.point)?r:null,conflicts:"ambiguous"===n.kind?n.conflicts:[],ambiguous:"ambiguous"===n.kind,contextKey:this._planSnapContextKey(s.key)}}_clearPlanSnapHover(t=!0){this.host._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),t&&(this.host._cursorPt=null)}_samePt(t,e){return rt(t,e)}_dropLegacySegments(t=this.host._serverCfg){for(const e of t?.markers||[])"ripple"===e.display&&(e.display="icon_ripple");for(const e of t?.spaces||[]){delete e.segments;const t=new Set,s=e=>"string"==typeof e&&e.length>=1&&e.length<=64&&!t.has(e),i=e=>(t.add(e),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=Ht);if(Array.isArray(e.partitions)&&(e.partitions=e.partitions.filter(t=>t&&s(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&i(t.id)).map(t=>({...t,cm:Number.isFinite(Number(t.cm))?Math.max(0,Math.min(100,Number(t.cm))):15})),e.partitions.length||delete e.partitions),Array.isArray(e.wall_columns)&&(e.wall_columns=e.wall_columns.filter(t=>t&&s(t.id)&&o(t.center)&&i(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qt(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:re(t.angle)}})),e.wall_columns.length||delete e.wall_columns),Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(t=>t&&s(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&i(t.id)).map(t=>{const e=tl(t);return{id:t.id,...e}}).filter(t=>t.points.length>=2),e.room_drafts.length||delete e.room_drafts),Array.isArray(e.walls)){const t=le(Pt({spaces:[e]}),e.id),s=t?he(e,t,lh,.02*$t).contour.map(t=>t.map(t=>t/lh)):ct(e.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);e.walls=wt(e.walls,e.rooms||[],U,1,s),e.walls.length||delete e.walls}}}_rollbackRejectedPhysicalWrites(t){if(!this.host._serverCfg||!t.length)return!1;let e=!1;for(const[s]of t){const t=this.host._pendingPhysicalWrites.get(s);t&&(e=this._restoreGeometryStateInConfig(this.host._serverCfg,t.before)||e,this.host._pendingPhysicalWrites.delete(s))}return!!e&&(this._clearGeometryGesture(),this.host._geometryHistory.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host._regSignature="",this.host._cfgContentFingerprint=_(this.host._serverCfg),this.host._maybeRebuildDevices(),this.host.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const t=this.host._writeChain;await t.catch(()=>{}),await this.host._reloadConfigOnly(!0)}_prepareConfigCandidate(t){return this._dropLegacySegments(t),ce(t)}_writeConfig(t=null){this.host._writesPending++,this.host._writeChain=de(this.host._writeChain,async()=>{if(!this.host._serverCfg)return;const e=_(this.host._serverCfg),s=this._prepareConfigCandidate(this.host._serverCfg),i=_(s);t&&e===t.attemptedFingerprint&&Object.assign(t,{revision:this.host._cfgRev,attempted:s,attemptedFingerprint:i});const o=[...this.host._pendingPhysicalWrites.entries()];for(const[t,e]of o){const i=s.spaces.find(e=>e.id===t),o=me(i);if(o===e.fingerprint)continue;let a=!1;try{a=this.host._checkSpacePhysicalGeometry(s,t).ok}catch{a=!1}if(!a)throw this._restoreGeometryStateLocal(e.before),this.host._pendingPhysicalWrites.delete(t),this.host._geometryHistory.clear(),this.host._showToast(this.host._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});e.fingerprint=o}i!==_(this.host._serverCfg)&&(this.host._serverCfg=s),this.host._cfgContentFingerprint=i;try{await this.host._sendConfigCandidate(s)}catch(t){if(!this._rollbackRejectedPhysicalWrites(o))throw t;const e=t&&"object"==typeof t?t:Object.assign(new Error(String(t)),{cause:t});throw e.physicalGeometryRolledBack=!0,e}for(const[t,e]of o)this.host._pendingPhysicalWrites.get(t)?.fingerprint===e.fingerprint&&this.host._pendingPhysicalWrites.delete(t)});return this.host._writeChain.finally(()=>{this.host._writesPending--})}_saveConfig(){this.host._cfgEpoch++,this.host._saveConfigDebounced()}_geometrySnapshotFromConfig(t,e){const s=t?.spaces?.find(t=>t.id===e);if(!s)return null;const i=t=>JSON.parse(JSON.stringify(t)),o={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==s[t]&&(o[t]=s[t]);return{spaceId:e,rooms:i(s.rooms||[]),...Array.isArray(s.openings)?{openings:i(s.openings)}:{},...Array.isArray(s.walls)?{walls:i(s.walls)}:{},...Array.isArray(s.wall_segments)?{wall_segments:i(s.wall_segments)}:{},...Array.isArray(s.open_spans)?{open_spans:i(s.open_spans)}:{},...Array.isArray(s.room_drafts)?{room_drafts:i(s.room_drafts)}:{},...Array.isArray(s.partitions)?{partitions:i(s.partitions)}:{},...Array.isArray(s.wall_columns)?{wall_columns:i(s.wall_columns)}:{},...Array.isArray(s.decor)?{decor:i(s.decor)}:{},plan_transform:o}}_geometrySnapshot(t=this.host._space){return this._geometrySnapshotFromConfig(this.host._serverCfg,t)}_recordGeometry(t,e){if(!e)return;const s=this._geometrySnapshot(e.spaceId);s&&JSON.stringify(e)!==JSON.stringify(s)&&(this.host._geometryHistory.push({name:t,before:e,after:s}),this.host.requestUpdate())}_restoreGeometryStateInConfig(t,e,s=!1){const i=t?.spaces?.find(t=>t.id===e.spaceId);if(!i)return!1;const o=t=>JSON.parse(JSON.stringify(t)),a=new Map((i.rooms||[]).map(t=>[t.id,t]));if(i.rooms=o(e.rooms),s)for(const t of i.rooms){const e=a.get(t.id);!Array.isArray(t.wall_ids)&&Array.isArray(e?.wall_ids)&&e.wall_ids.length===t.poly?.length&&(t.wall_ids=o(e.wall_ids))}const n=(t,e)=>{void 0!==e?i[t]=o(e):s&&"wall_segments"===t||delete i[t]},r=new Map((i.openings||[]).map(t=>[t.id,t])),l=new Map((i.room_drafts||[]).map(t=>[t.id,t]));if(n("openings",e.openings),n("walls",e.walls),n("wall_segments",e.wall_segments),n("open_spans",e.open_spans),n("room_drafts",e.room_drafts),n("partitions",e.partitions),n("wall_columns",e.wall_columns),n("decor",e.decor),s){for(const t of i.openings||[]){const e=r.get(t.id);t.host||"wall"!==e?.host?.kind||(t.host=o(e.host))}for(const t of i.room_drafts||[]){const e=l.get(t.id);for(let s=0;s<(t.segments||[]).length;s++)!t.segments[s]?.id&&e?.segments?.[s]?.id&&(t.segments[s].id=e.segments[s].id)}}for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete i[t];return Object.assign(i,o(e.plan_transform||{})),!0}_restoreGeometryStateLocal(t){return!!this._restoreGeometryStateInConfig(this.host._serverCfg,t)&&(this.host._cfgEpoch++,this.host._modelCache=null,this.host._wallUnionCache=null,this.host._physicalBodiesCache=null,this.host._frame=null,this.host.requestUpdate(),!0)}_wallModelBlockerLabel(t){const e=t instanceof Ur?t.reason:"invalid-room";return this.host._t(`wall_model.reason.${e}`)}_hasLegacyZeroWallFields(t=this.host._serverCfg){return(t?.spaces||[]).some(t=>Array.isArray(t?.open_spans)&&t.open_spans.length>0||(t?.rooms||[]).some(t=>Array.isArray(t?.open_to)&&t.open_to.length>0))}_showWallModelMigrationBlocked(t){const e=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this.host._showToast(this.host._t(e,{reason:this._wallModelBlockerLabel(t)}))}_limitSegmentsOf(t){const e=[];for(const s of t?.wall_segments||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.partitions||[])s?.a&&s?.b&&e.push({id:String(s.id||""),a:s.a,b:s.b,cm:Number(s.cm)});for(const s of t?.room_drafts||[]){const t=Array.isArray(s?.points)?s.points:[],i=Array.isArray(s?.segments)?s.segments:[];for(let o=0;o+1t?.id===e);if(!i)return[];const o=Number(i.cell_cm)>0?Number(i.cell_cm):5,a=this._limitSegmentsOf(i),n=[...qa(a),...Ya(a,o,U),...Xa(a,o,U)];let r=null;try{r=pe(i.rooms||[],i.walls||[],[],U,o,U,1)}catch{r=null}let l="ok"===s?.status||"degraded-extra"===s?.status?s.roomGeom:null;if(!l&&r&&r.nodes.length)try{const t=ue(i.rooms||[],i.walls||[],[],[],U,o,U,1);l="ok"===t?.status||"degraded-extra"===t?.status?t.roomGeom:null}catch{l=null}for(const t of i.rooms||[]){const e=String(t?.id||"");if(!e)continue;let s=null;try{s=_e(i.rooms||[],e,i.walls||[],[],U,o,U,1,l??void 0,r)}catch{s=null}n.push(...Qa(e,s,o,U))}return n}_junctionLimitLabel(t){const e=t=>String(Math.round(10*t)/10);return this.host._t(`junction.limit_${t.rule}`,{actual:e(t.actual),limit:e(t.limit)})}_junctionLimitsIntroduced(t,e,s,i){let o;const a=(e?.spaces||[]).find(t=>t?.id===s);let n="";try{n=me(a)}catch{n=""}const r=e&&"object"==typeof e?this._junctionBaselineCache.get(e):void 0;if(r&&n&&r.fingerprint===n&&r.spaceId===s&&(o=r.violations),!o){try{const t=Number(e?.model_version||0)>=9?e:fl(e).config;o=this.host._junctionLimitViolations(t,s)}catch{return[]}e&&"object"==typeof e&&n&&this._junctionBaselineCache.set(e,{spaceId:s,fingerprint:n,violations:o})}let l=[];try{l=this.host._junctionLimitViolations(t,s,i)}catch{return[{rule:"check_failed",subject:s,actual:0,limit:0}]}return function(t,e){const s=new Map;for(const t of e||[])s.set(t.rule,(s.get(t.rule)||0)+1);const i=new Map;for(const e of t||[])i.set(e.rule,[...i.get(e.rule)||[],e]);const o=[];for(const[t,e]of i){const i=e.length-(s.get(t)||0);i>0&&o.push(...e.slice(0,i))}return o}(l,o)}_commitPhysicalGeometry(t,e,s=[]){if(!e||!this.host._serverCfg)return!1;const i=this.host._serverCfg,o=this._geometrySnapshotFromConfig(i,e.spaceId),a=i.spaces.find(t=>t.id===e.spaceId);if(!a||me(e)===me(a))return!1;let n,r=!1;try{r=this.host._checkSpacePhysicalGeometry(i,e.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;let l=e;try{if(Number(i.model_version||0)<9){const t=JSON.parse(JSON.stringify(i));if(!this._restoreGeometryStateInConfig(t,e))throw new Ur("invalid-room",e.spaceId);const s=fl(t).config;l=this._geometrySnapshotFromConfig(s,e.spaceId)||e;const a=JSON.parse(JSON.stringify(s));if(!o||!this._restoreGeometryStateInConfig(a,o,!0))throw new Ur("invalid-room",e.spaceId);const r=s.spaces.find(t=>t.id===e.spaceId),h=a.spaces.find(t=>t.id===e.spaceId);if(!Array.isArray(o.wall_segments)&&JSON.stringify(e.rooms)===JSON.stringify(o.rooms))for(const t of h?.wall_segments||[])t.cm=mt(o.walls,t.a,t.b,U,1);const c=((t,e,s)=>{const i=new Map,o=new Map((t?.rooms||[]).map(t=>[t.id,t])),a=new Map((e||[]).map(t=>[t.id,t])),{rooms:n}=pl(s,cl(t));for(const t of n){const e=a.get(t.id),n=o.get(t.id),r=V(e),l=(s.rooms||[]).find(e=>e.id===t.id),h=V(l),c=V(n);if(r&&h&&c&&r.length===h.length&&c.length===n?.wall_ids?.length&&t.poly.length===t.wall_ids.length)for(let e=0;e{const o=[];for(let a=0;aqr||il(r,s,i)>qr)continue;const l=sl(n,s,i),h=sl(r,s,i);l<-qr||h>1+qr||h<=l+qr||o.push({start:l,id:e[a]})}return o.sort((t,e)=>t.start-e.start)},o=s(c,n.wall_ids,r[e],r[(e+1)%r.length]),a=s(t.poly,t.wall_ids,h[e],h[(e+1)%h.length]);if(o.length&&o.length===a.length)for(let t=0;tt.id===e.spaceId),i=this.host._path.length>=2?this.host._path.map(t=>[t[0]/lh,t[1]/lh]):[];i.push(...s.map(t=>[t[0],t[1]])),h=el(t)<=el(l,i)&&this.host._checkSpacePhysicalGeometry(n,e.spaceId).ok}catch{h=!1}if(!h)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;const c=JSON.parse(JSON.stringify(i));this._restoreGeometryStateInConfig(c,e);const d=this._junctionLimitsIntroduced(n,c,e.spaceId);if(d.length)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(e),this.host._showToast(this._junctionLimitLabel(d[0])),!1;vl(i,n),this._recordGeometry(t,l);const p=i.spaces.find(t=>t.id===e.spaceId),u=this.host._pendingPhysicalWrites.get(e.spaceId);return this.host._pendingPhysicalWrites.set(e.spaceId,{before:u?.before||l,fingerprint:me(p)}),this._saveConfig(),!0}_clearGeometryGesture(){this._clearFurniturePreview(),this.host._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),this.host._mergeSel=null,this.host._mergeDialog=null,this.host._splitSel=null,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._wallRepairDiagnostic=null,this.host._roomDeleteDialog=null,this.host._wallDialog=null,this.host._physicalDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._openingDialog=null,this._rszResetController(),this.host._decorDraft=null,this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this.host._kioskHoldTimer),this.host._swipeStart?.id===t.pointerId&&(this.host._swipeStart=null),this.host._furnTouchPending?.pid===t.pointerId)return this._clearFurniturePreview(),this.host._pointers.delete(t.pointerId),void this.host.requestUpdate();if(this.host._physicalDrag?.pid===t.pointerId||this.host._physicalRotate?.pid===t.pointerId)return void this._cancelPhysicalGesture();if(this.host._decorDraft?.pid===t.pointerId)return this.host._decorDraft=null,void this.host.requestUpdate();if(this.host._decorMove?.pid===t.pointerId||this.host._dtDrag?.pid===t.pointerId||this.host._bdDrag?.pid===t.pointerId)return void this._cancelDecorGesture();"opening"===this.host._tool?(this.host._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this.host._tool&&this._clearPlanSnapHover();const e=!!this.host._pinchStart||!!this.host._panStart;this.host._pointers.delete(t.pointerId),this.host._pointers.size<2&&(this.host._pinchStart=null),0===this.host._pointers.size&&(this.host._panStart=null,this.host._panLock=null),e&&0===this.host._pointers.size&&this.host.requestUpdate()}_applyGeometryState(t,e=!1){if(!this.host._canCommitSpace(t.spaceId))return!1;const s=this._geometrySnapshot(t.spaceId);if(!s||!this._restoreGeometryStateLocal(t))return!1;const i=this.host._serverCfg,o=me(s)!==me(t);if(o){let o=!1;try{const s=i?this.host._checkSpacePhysicalGeometry(i,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1}let a;try{a=fl(i).config}catch(t){return this._restoreGeometryStateLocal(s),this._showWallModelMigrationBlocked(t),!1}if(o){let o=!1;try{const s=a?this.host._checkSpacePhysicalGeometry(a,t.spaceId):null;o=!!s?.ok||!(!e||"wall-degraded-extra"!==s?.reason)}catch{o=!1}if(!o)return this._restoreGeometryStateLocal(s),this.host._showToast(this.host._t("toast.geometry_unsafe")),!1;vl(i,a);const n=i?.spaces.find(e=>e.id===t.spaceId),r=this.host._pendingPhysicalWrites.get(t.spaceId);this.host._pendingPhysicalWrites.set(t.spaceId,{before:r?.before||s,fingerprint:me(n)})}else vl(i,a);return this._clearGeometryGesture(),this.host._space!==t.spaceId&&(this.host._commitSpace(t.spaceId),this.host._saveNav(),this.host._restoreZoom()),this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate(),!0}_roomAt(t){return this.host._spaceModel()?.rooms.find(e=>{const s=V(e);return!!s&&ge(t,s)})}_overlapRoom(t){return this.host._spaceModel()?.rooms.find(e=>{const s=V(e);return!!s&&I(t,s)})}_pointInRoom(t,e){return e.poly?fe(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!ta(t))return!0;const e=t.length,s=.001;for(let i=0;i=500)return this.host._showToast(this.host._t("toast.physical_limit")),!1;const t=this.host._curSpaceCfg;return!(!this.host._activeDraftId&&(t?.room_drafts||[]).length>=200||this._draftSegmentCount(t)>=2e3||(t?.partitions||[]).length+this.host._path.length>hh)||(this.host._showToast(this.host._t("toast.physical_limit")),!1)}_markupClick(t){if(this.host._vacFit)return;if(!this.host._markup)return;const e=this.host._spaceModel();if(!e)return void this._clearGeometryGesture();if(this.host._suppressClick)return;if(this.host._drag||this.host._rlResize)return;const s=t.composedPath?.()||[];if(s.some(t=>t?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(s.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this.host._tool)return void(this.host._physicalSel=null);if("resize"===this.host._tool){if(this.host._resize.dragging||s.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...e.rooms].reverse().find(t=>this._pointInRoom(i,t));return this.host._resize.selectRoom(t?.id||null),void this.host.requestUpdate()}if("delroom"===this.host._tool)return void this._deleteRoomClick(i);if("opening"===this.host._tool)return void this._openingClick(i);if("merge"===this.host._tool)return void this._mergeClick(i);if("wallthick"===this.host._tool)return void this._wallThickClick(i);if("split"===this.host._tool)return void this._splitClick(i);if("column"===this.host._tool)return void this._columnClick(i);this.host._wallRepairDiagnostic=null;const o=this._resolvePlanDrawPoint(i,t.shiftKey);if(o.ambiguous)return this.host._planSnapHover={contextKey:o.contextKey,candidate:null,conflicts:o.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(o.conflicts),void this.host._showToast(this.host._t("toast.plan_snap_ambiguous"));let a=o.point;if(t.ctrlKey||t.metaKey){if(t.preventDefault(),this.host._path.length<3)return;a=ne(this.host._path[this.host._path.length-1],this.host._path[0])}if(!this.host._path.length){if(!t.shiftKey&&!o.candidate&&this._offerExistingWallFace(i))return;const e=this._draftEndAt(a);return e?(this.host._activeDraftId=e.draft.id,this.host._resumeDraftBySpace[this.host._space]=e.draft.id,this.host._path=e.reverse?[...e.draft.points].reverse().map(t=>[...t]):e.draft.points.map(t=>[...t]),void(this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.reverse?[...e.draft.segments].reverse().map(t=>t.cm):e.draft.segments.map(t=>t.cm),e.draft.id))):(this.host._activeDraftId=null,this.host._draftSegmentCms=[],void(this.host._path=[a]))}const n=this.host._path[this.host._path.length-1];if(this._samePt(a,n))return;const r=this._draftEndAt(a,this.host._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this.host._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const h=this.host._path.map(t=>[...t]);this.host._path=[...this.host._path,a],this.host._draftSegmentCms=[...this.host._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(h)}_draftEndAt(t,e){const s=this.host._spaceModel();if(!s)return null;const i=this.host._viewOr(this.host._baseVb()),o=Math.max(.15*this.host._gridPitch,this.host._stageEl?.clientWidth?i.w/this.host._stageEl.clientWidth*12:0);for(const i of s.room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const s=i.points[0],a=i.points[i.points.length-1];if(Math.hypot(t[0]-a[0],t[1]-a[1])<=o)return{draft:i,reverse:!1};if(Math.hypot(t[0]-s[0],t[1]-s[1])<=o)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this.host._curSpaceCfg;if(!e||!this.host._path.length)return;const s=this.host._path.map(t=>[...t]),i=this._wallGraphSources(s),o=Array.isArray(e.room_drafts)?e.room_drafts:[],a=this.host._activeDraftId?o.find(t=>t.id===this.host._activeDraftId):null,n=o.find(e=>e.id===t.draft.id);if(!n)return;const r=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),l=t.reverse?(n.segments||[]).map(t=>({...t})):[...n.segments||[]].reverse().map(t=>({...t})),h=this.host._path[this.host._path.length-1],c=this._samePt(h,r[0]),d=c?null:this.host._drawWallCm;if(!c&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this.host._path,a,this.host._draftSegmentCms),u=[...this.host._path.map(t=>[...t]),...c?r.slice(1):r],_=[...p,...null==d?[]:[{cm:d}],...l],m=u.length>=4&&this._samePt(u[0],u[u.length-1]),g=u,f=_;if(g.length>500)return void this.host._showToast(this.host._t("toast.physical_limit"));const b=(a?.segments?.length||0)+(n.segments?.length||0);if(this._draftSegmentCount(e)-b+f.length>2e3)return void this.host._showToast(this.host._t("toast.physical_limit"));if(m){const t=g.slice(0,-1);if(this._contourSelfIntersects(t)||R(t)<=1e-6)return void this.host._showToast(this.host._t("toast.contour_cannot_close"));const e=this._overlapRoom(t);if(e)return void this.host._showToast(this.host._t("toast.room_overlap",{name:e.name||""}))}const v=this._geometrySnapshot(),y=this.host._activeDraftId||t.draft.id,$={...a||n,id:y,points:g.map(t=>[t[0]/lh,t[1]/lh]),segments:f};e.room_drafts=o.filter(e=>e.id!==t.draft.id&&(!this.host._activeDraftId||e.id!==this.host._activeDraftId)),e.room_drafts.push($),this.host._activeDraftId=y,this.host._resumeDraftBySpace[this.host._space]=y,this.host._path=g,this.host._draftSegmentCms=this._adoptDraftCms(g,f.map(t=>Number(t.cm)),y),this.host._physicalSel=null,this._commitPhysicalGeometry(this.host._t("history.draft_merge"),v),null!=d&&this._offerWallFaces(s,s.length-1,i)}_adoptDraftCms(t,e,s){const i=Math.max(0,t.length-1),o=xn(i,e,this.host._drawWallCm,X);return e.length!==i&&console.debug(`[houseplan] draft ${s??"?"}: восстановлено толщин ${i-e.length} (#234)`),o}_draftSegmentsForPath(t,e,s){const i=Array.isArray(e?.points)?e.points.map(t=>[Number(t?.[0])*lh,Number(t?.[1])*lh]):[],o=Array.isArray(e?.segments)?e.segments:[];return xn(Math.max(0,t.length-1),s,this.host._drawWallCm,X).map((e,s)=>{const a=t[s],n=t[s+1],r=i.findIndex((t,e)=>{if(e+1>=i.length)return!1;const s=i[e+1];return this._samePt(a,t)&&this._samePt(n,s)||this._samePt(a,s)&&this._samePt(n,t)});return{...r>=0?o[r]:{},cm:e}})}_persistActiveDraftSegment(){if(this.host._path.length<2||!this.host._curSpaceCfg)return;const t=this._geometrySnapshot(),e=this.host._curSpaceCfg;e.room_drafts||=[],this.host._activeDraftId||(this.host._activeDraftId="draft-"+Date.now().toString(36)),this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId;const s=e.room_drafts.findIndex(t=>t.id===this.host._activeDraftId),i={...s>=0?e.room_drafts[s]:{},id:this.host._activeDraftId,points:this.host._path.map(t=>[t[0]/lh,t[1]/lh]),segments:this._draftSegmentsForPath(this.host._path,s>=0?e.room_drafts[s]:null,this.host._draftSegmentCms)};s>=0?e.room_drafts[s]=i:e.room_drafts.push(i),this._commitPhysicalGeometry(this.host._t("history.draft_segment"),t)}_activeWallSourceKey(t){return`active:${this.host._activeDraftId||"session"}:${t}`}_wallGraphSources(t){const e=this._planStructuralGeometrySnapshot().value.segments.map(t=>({a:t.a,b:t.b,key:`static:${t.key}`}));for(let s=0;s+1t.key===s);if(i>=0){const[t]=this.host._wallFaceGraphCache.splice(i,1);return this.host._wallFaceGraphCache.push(t),t.value}const o=Kn(t,e);return this.host._wallFaceGraphCache.push({key:s,value:o}),this.host._wallFaceGraphCache.length>4&&this.host._wallFaceGraphCache.shift(),o}_offerWallFaces(t,e=this.host._path.length-2,s){if(this.host._path.length<2||this.host._wallFaceBatch||this.host._roomDialog)return;const i=this.host._spaceModel();if(!i)return;const o=this._activeWallSourceKey(e),a=2e-4*this.host._gridPitch;let n,r;try{const e=s||this._wallGraphSources(t);r=this._wallGraphSources(this.host._path),n=function(t,e,s){const i=new Set(t.faces.map(t=>t.key));return e.faces.filter(t=>!i.has(t.key)&&t.sourceKeys.includes(s))}(this._wallFaceGraph(e,a),this._wallFaceGraph(r,a),o)}catch{return}if(!n.length){const t=ar(r,{requiredSourceKey:o,maxDistance:nt(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:a});return"ambiguous"===t.kind?(this.host._wallRepairDiagnostic=t.proposals[0]||null,void this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==t.kind||this._overlapRoom(t.face.ring)||this._beginWallFaceBatch([{...t.face,repair:t.proposal}]))}for(const t of i.rooms){const e=V(t);if(!t.id||!e)continue;const s=be(e,this.host._path,.02*this.host._gridPitch);if(!s)continue;const[i,o]=s,a=R(i)>=R(o)?i:o,r=a===i?o:i,l=[...n].sort((t,e)=>Math.abs(t.area-R(r))-Math.abs(e.area-R(r))||t.key.localeCompare(e.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(t=>[t[0],t[1]]),split:{roomId:t.id,mainPoly:a,newPoly:r},consumeAllActive:!0}])}const l=n.filter(t=>!(t.ring.length<3||this._contourSelfIntersects(t.ring))&&!this._overlapRoom(t.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(t){this.host._wallRepairDiagnostic=null,this.host._wallFaceBatch={candidates:[...t].sort((t,e)=>t.area-e.area||t.key.localeCompare(e.key)),index:0,decisions:[],activePath:this.host._path.map(t=>[...t]),activeCms:[...this.host._draftSegmentCms],activeDraftId:this.host._activeDraftId},this._clearPlanSnapHover(),this.host._nameSel="",this.host._areaSel="",this._resetRoomDialogFields(),this.host._roomDialog=!0}_offerExistingWallFace(t){if(this.host._path.length||this.host._wallFaceBatch||this.host._roomDialog)return!1;const e=2e-4*this.host._gridPitch;try{let s,i=Vn(this._wallFaceGraph(this._wallGraphSources([]),e),t,e);if(!i){const o=ar(this._wallGraphSources([]),{point:t,maxDistance:nt(2,this.host._cellCm,this.host._gridPitch),gridStep:this.host._gridPitch,epsilon:e});if("ambiguous"===o.kind)return this.host._wallRepairDiagnostic=o.proposals[0]||null,this.host._showToast(this.host._t(0===this.host._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===o.kind)i=o.face,s=o.proposal;else if("none"===o.kind){const s=ar(this._wallGraphSources([]),{point:t,maxDistance:this._cssPxToRender(12),gridStep:this.host._gridPitch,epsilon:e});if("repair"===s.kind)return this.host._wallRepairDiagnostic=s.proposal,this.host._showToast(this.host._t("toast.wall_repair_too_large")),!0}}return!(!i||i.ring.length<3||this._contourSelfIntersects(i.ring)||this._overlapRoom(i.ring))&&(this._beginWallFaceBatch([{...i,existing:!s,repair:s}]),!0)}catch{return!1}}_columnClick(t){const e=this._snap(t),s=this.host._drawWallCm;if(null==s)return void this._showPhysicalRange(Wt,1);if(!this.host._curSpaceCfg||this._limitReached("column"))return;const i=this.host._spaceModel();if(!i)return;const o={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qt(s),angle:0},a=(i.wall_columns||[]).find(t=>jt(t,o,.02*this.host._gridPitch));if(a)return clearTimeout(this.host._duplicateColumnTimer),this.host._duplicateColumnId=a.id,this.host._duplicateColumnTimer=window.setTimeout(()=>{this.host._duplicateColumnId=null},900),void this.host._showToast(this.host._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this.host._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...o,center:[e[0]/lh,e[1]/lh]}),this._commitPhysicalGeometry(this.host._t("history.column_add"),n)}_openPhysicalDialog(t,e,s){const i=this.host._spaceModel();if(i)if("partition"===t){const s=i.partitions.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:se(s.cm,this.host._imperial),length:this.host._fmtLen(s.a,s.b)})}else if("column"===t){const s=i.wall_columns.find(t=>t.id===e);s&&(this.host._physicalDialog={kind:t,id:e,cm:se(s.cm,this.host._imperial),shape:s.shape,angle:this.host._angleField("square"===s.shape?re(s.angle):0)})}else{const o=i.room_drafts.find(t=>t.id===e),a=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,s||0));o?.segments[a]&&(this.host._physicalDialog={kind:t,id:e,segment:a,cm:se(o.segments[a].cm,this.host._imperial),length:this.host._fmtLen(o.points[a],o.points[a+1])})}}_physicalDown(t,e,s){const i=this.host._spaceModel();if(!i)return;t.stopPropagation(),ch(t);const o=this._svgPoint(t),a=[];for(const t of[...i.wall_columns].reverse())ve(o,ye(t,this.host._cellCm,this.host._gridPitch))&&a.push({kind:"column",id:t.id});for(const t of[...i.partitions].reverse()){const e=$e(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);e&&ve(o,e)&&a.push({kind:"partition",id:t.id})}a.some(t=>t.kind===e&&t.id===s)||a.unshift({kind:e,id:s});const n=a.map(t=>`${t.kind}:${t.id}`).sort().join("|"),r=performance.now(),l=this.host._physicalPickCycle,h=a.length>1&&l?.signature===n&&r-l.at>380&&r-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%a.length:Math.max(0,a.findIndex(t=>t.kind===e&&t.id===s));this.host._physicalPickCycle={signature:n,index:h,x:t.clientX,y:t.clientY,at:r},e=a[h].kind,s=a[h].id;const c="partition"===e?i.partitions.find(t=>t.id===s):i.wall_columns.find(t=>t.id===s);c&&(this.host._physicalSel={kind:e,id:s},this.host._physicalDrag={pid:t.pointerId,kind:e,id:s,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,s){const i="partition"===t?[e.a,e.b]:[e.center],o=i.map(t=>t[0]),a=i.map(t=>t[1]);return[Math.max(-Ht*lh-Math.min(...o),Math.min(Ht*lh-Math.max(...o),s[0])),Math.max(-Ht*lh-Math.min(...a),Math.min(Ht*lh-Math.max(...a),s[1]))]}_physicalMove(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const s=this._svgPoint(t),i="partition"===e.kind?e.base.a:e.base.center,o=this._snap([i[0]+s[0]-e.start[0],i[1]+s[1]-e.start[1]]),a=this._clampPhysicalDelta(e.kind,e.base,[o[0]-i[0],o[1]-i[1]]);this.host._physicalDrag={...e,delta:a,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this.host._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this.host._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);const s=this.host._spaceModel();if(!this.host._curSpaceCfg||!s)return;const i=this.host._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;if(t){const o={...s,a:[s.a[0]+e.delta[0],s.a[1]+e.delta[1]],b:[s.b[0]+e.delta[0],s.b[1]+e.delta[1]]};t.a=[o.a[0]/lh,o.a[1]/lh],t.b=[o.b[0]/lh,o.b[1]/lh];for(const t of i.openings||[]){if("partition"!==t.host?.kind||t.host.id!==e.id)continue;const s=J(t,[o],lh,this.host._cellCm,this.host._gridPitch).resolved;s&&Object.assign(t,Y(t,s,lh))}}}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),o=e.base,a={...o,center:[o.center[0]+e.delta[0],o.center[1]+e.delta[1]]};if(s.wall_columns.some(t=>t.id!==e.id&&jt(t,a,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));t&&(t.center=[(o.center[0]+e.delta[0])/lh,(o.center[1]+e.delta[1])/lh])}this._commitPhysicalGeometry(this.host._t("history.physical_move"),e.before)}_registerPhysicalTap(t,e,s){const i=performance.now(),o=this.host._physicalLastTap?.kind===t&&this.host._physicalLastTap.id===e&&this.host._physicalLastTap.segment===s&&i-this.host._physicalLastTap.at<=360;this.host._physicalLastTap={kind:t,id:e,segment:s,at:i},o&&(this.host._physicalLastTap=null,this._openPhysicalDialog(t,e,s))}_cancelPhysicalGesture(){this.host._physicalDrag=null,this.host._physicalRotate=null,this.host.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape||!this.host._spaceModel())return;t.preventDefault(),t.stopPropagation(),ch(t);const s=this._svgPoint(t);this.host._physicalSel={kind:"column",id:e.id},this.host._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,baseAngle:re(e.angle),angle:re(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const s=this._svgPoint(t),i=180*Math.atan2(s[1]-e.center[1],s[0]-e.center[0])/Math.PI,o=e.baseAngle+i-e.startAngle,a=re(t.shiftKey?o:5*Math.round(o/5));this.host._physicalRotate={...e,angle:a,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this.host._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation(),this.host._physicalRotate=null;const s=this.host._spaceModel();if(!e.moved||!this.host._curSpaceCfg||!s)return;const i=s.wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const o={...i,angle:e.angle};if(s.wall_columns.some(t=>t.id!==e.id&&jt(t,o,.02*this.host._gridPitch)))return void this.host._showToast(this.host._t("toast.column_duplicate"));const a=this.host._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);a&&(a.angle=e.angle,this._commitPhysicalGeometry(this.host._t("history.physical_edit"),e.before))}_rszRooms(){const t=[],e=this.host._spaceModel();if(!e)return t;for(const s of e.rooms){const e=s.id?V(s):null;e&&t.push({id:s.id,poly:e,wall_ids:Array.isArray(s.wall_ids)?[...s.wall_ids]:void 0})}return s=t,i=Math.max(1e-12,1e-12*this.host._gridPitch),s.map(t=>{if(!Array.isArray(t.wall_ids)||t.wall_ids.length!==t.poly.length)return{...t,poly:t.poly.map(t=>[...t])};const e=t.poly.map(t=>[...t]);let s=!0;for(;s&&e.length>3;){s=!1;for(let t=0;t0){e.splice(t,1),s=!0;break}}}return{...t,poly:e}});var s,i}_rszOpenings(){return this.host._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen,hosted:!!t.host,angle:t.angle,type:t.type}))}_rszObstacles(){const t=this.host._spaceModel();if(!t)return[];const e=[];for(const s of t.partitions||[])e.push({kind:"segment",a:[...s.a],b:[...s.b],half:nt(s.cm,this.host._cellCm,this.host._gridPitch)/2});for(const s of t.room_drafts||[])for(let t=0;t+1Array.isArray(t.a)&&Array.isArray(t.b)&&t.a.length>=2&&t.b.length>=2).map(t=>({wall:t,a:[t.a[0]*lh,t.a[1]*this.host._spaceH],b:[t.b[0]*lh,t.b[1]*this.host._spaceH]})),o=Math.abs(t[0]-e[0])<=.05*this.host._gridPitch?"v":Math.abs(t[1]-e[1])<=.05*this.host._gridPitch?"h":null,a=i.filter(s=>!!o&&("h"===o?!(Math.abs(s.a[1]-t[1])>.05*this.host._gridPitch||Math.abs(s.b[1]-t[1])>.05*this.host._gridPitch)&&Math.min(Math.max(t[0],e[0]),Math.max(s.a[0],s.b[0]))-Math.max(Math.min(t[0],e[0]),Math.min(s.a[0],s.b[0]))>.05*this.host._gridPitch:!(Math.abs(s.a[0]-t[0])>.05*this.host._gridPitch||Math.abs(s.b[0]-t[0])>.05*this.host._gridPitch)&&Math.min(Math.max(t[1],e[1]),Math.max(s.a[1],s.b[1]))-Math.max(Math.min(t[1],e[1]),Math.min(s.a[1],s.b[1]))>.05*this.host._gridPitch)),n=new Set(a.map(t=>Number(t.wall.cm)).filter(t=>t>0));return{minDim:this.host._cmToUnits(30),eps:.05*this.host._gridPitch,step:this.host._gridPitch,movingHalf:s>0?nt(s,this.host._cellCm,this.host._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:n.size>1}}_rszResolution(t,e,s){const i=this.host._resize.snapshotIdentity||s||this._rszSnapshot(),o=`${this.host._space}|${this.host._cellCm}|${this.host._gridPitch}|${i}`,a=`${t}:${e}`;return this.host._resize.resolve(o,a,()=>{const s=[...this.host._resize.rooms||this._rszRooms()],i=s.find(e=>e.id===t),o=i?.poly?.[e]||[0,0],a=i?.poly?.[(e+1)%(i?.poly?.length||1)]||[0,0];return function(t,e,s,i,o){const{eps:a}=o,n=t.find(t=>t.id===s);if(!n||i<0||i>=(n.poly?.length||0)||n.poly.length<4||!ta(n.poly))return{enabled:!1,reason:"invalid-geometry"};const r=n.poly[i],l=n.poly[(i+1)%n.poly.length],h=oa(r,l,a);if(!h)return{enabled:!1,reason:"diagonal"};if(!la(n.poly,i,h,a))return{enabled:!1,reason:"side-angle"};if(o.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const t of o.obstacles||[])if(da(t,r,l,o))return{enabled:!1,reason:"duplicate-physical-wall"};const c=Yo(Ko(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const e of t)if(e.id!==s)for(let t=0;t1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[s]:i},g=[s];if(1===d.length){const t=d[0];if(!ta(t.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!la(t.room.poly,t.edge,h,a))return{enabled:!1,reason:"side-angle"};g.push(t.room.id),m[t.room.id]=t.edge}const f=[];for(const t of e)if(!(Xo([t.x,t.y],r,l)>2*a)){if(t.hosted||t.length>c+2*a)return{enabled:!1,reason:"opening-conflict"};f.push(t.id)}const b=Object.fromEntries(g.map(e=>[e,t.find(t=>t.id===e).poly.length])),v=[];for(const e of g){const s=t.find(t=>t.id===e),i=m[e],o=(i-1+s.poly.length)%s.poly.length,n=(i+1)%s.poly.length,r=_a(t,e,o,1,a),l=_a(t,e,n,0,a);if(!r||!l)return{enabled:!1,reason:"partial-shared"};v.push(r,l)}const y={roomId:s,edge:i,a:[...r],b:[...l],n:Qo(n.poly,i),roomIds:g,edgeByRoom:m,topology:b,movingOpeningIds:f,sideOwnership:v};if(!$a(t,e,y,0,o))return{enabled:!1,reason:"invalid-geometry"};const $=Math.abs(Number(o.step));if(Number.isFinite($)&&$>a){const s=[-$,$];if(!s.some(s=>$a(t,e,y,s,o))){if(!s.some(s=>fa(ba(t,e,y,s),y,a)))return{enabled:!1,reason:"partial-shared"};const i={...o,obstacles:[]};return s.some(s=>$a(t,e,y,s,i))?{enabled:!1,reason:"duplicate-physical-wall"}:e.length&&s.some(e=>$a(t,[],y,e,o))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:y}}(s,[...this.host._resize.openings||this._rszOpenings()],t,e,this._rszOptsFor(o,a))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this.host._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const t=null!==this.host._resize.preview;this.host._resize.reset(),t&&this.host._cfgEpoch++}_rszProjectPreview(t,e,s,i,o){this.host._rszLimitViolation=null;const a=this.host._serverCfg?.spaces.find(t=>t.id===this.host._space);if(!a||!this.host._serverCfg)return{ok:!1,reason:"missing-context"};const n=JSON.parse(t),r={...a,rooms:n.rooms,openings:n.openings||[]};for(const t of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==n[t]?r[t]=n[t]:delete r[t];for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==n.plan_transform?.[t]?r[t]=n.plan_transform[t]:delete r[t];const l=this.host._spaceH;for(const[t,s]of Object.entries(e)){const e=r.rooms.find(e=>e.id===t);e&&(e.poly=s.map(t=>[t[0]/lh,t[1]/l]),delete e.x,delete e.y,delete e.w,delete e.h)}for(const[t,e]of Object.entries(s)){const s=(r.openings||[]).find(e=>e.id===t);s&&(s.x=e[0]/lh,s.y=e[1]/l)}const h=[],c=[];for(const t of i){const e=o.find(e=>e.id===t),s=r.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const i=s.poly.map(t=>[t[0]*lh,t[1]*l]);if(e.poly.length!==i.length)continue;const a=Array.isArray(e.wall_ids)?e.wall_ids:[];for(let t=0;tt.id===s):null;o&&(o.a=[i[t][0]/lh,i[t][1]/lh],o.b=[i[(t+1)%i.length][0]/lh,i[(t+1)%i.length][1]/lh])}}if(h.length&&Array.isArray(r.walls)&&r.walls.length){const t=we(r.walls,h,c,this.host._wallKeyPitch,lh,"fixed-topology");if(t.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=t.walls}const d=[];for(const t of r.rooms||[]){const e=V(t);if(e&&!(e.length<2))for(let t=0;tJSON.stringify([t?.key,t?.cm,t?.a,t?.b]),u=new Map;for(const t of n.walls||[]){const e=p(t);u.set(e,(u.get(e)||0)+1)}const _=[];for(const t of r.walls||[]){const e=p(t),s=u.get(e)||0;s?u.set(e,s-1):_.push(t)}if(ke(_,d,this.host._wallKeyPitch,lh,n.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this.host._space,r);if(!m.ok)return{ok:!1,reason:"physical-geometry"};const g={...this.host._serverCfg,spaces:(this.host._serverCfg?.spaces||[]).map(t=>t?.id===this.host._space?r:t)},f=this._junctionLimitsIntroduced(g,this.host._serverCfg,this.host._space,m.wallGeometry);return f.length?(this.host._rszLimitViolation=f[0],{ok:!1,reason:"junction-limit"}):{ok:!0,value:{preview:{space:this.host._space,sp:r},beforeWalls:n.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}}_rszAcceptPreview(t,e){if(this.host._cfgEpoch++,!t||!e)return;const s=xe(e);if(!s)return;const i=`${this.host._space}|${this.host._cfgEpoch}|${t.sp.rooms.length}`;Object.defineProperty(s,"sourceFingerprint",{value:_([t.sp,this.host._cellCm,this.host._gridPitch]),enumerable:!1});const o={key:i,value:s};rh(this.host._wallUnionPool,i,o,8),this.host._wallUnionCache=o}_rszSpaceCandidateGeometry(t,e){if(!this.host._serverCfg)return{ok:!1,wallGeometry:null};const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};let i=null;try{return{ok:this.host._checkSpacePhysicalGeometry(s,t,t=>{i=t}).ok,wallGeometry:i}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(t,e){if(!this.host._serverCfg)return!1;try{const s={...this.host._serverCfg,spaces:this.host._serverCfg.spaces.map(s=>s.id===t?e:s)};return this.host._checkSpacePhysicalGeometry(s,t).ok}catch{return!1}}_rszCandidateRenderable(t){return!!t&&t.space===this.host._space&&this._rszSpaceCandidateRenderable(t.space,t.sp)}_rszEdgeDown(t,e,s){if("resize"!==this.host._tool||this.host._resize.dragging)return;t.stopPropagation(),t.preventDefault();const i=this._rszRooms(),o=this._rszResolution(e,s);if(!o.enabled)return void this.host._showToast(this._rszReasonText(o.reason));ch(t);const a=o.plan,n=this._svgPoint(t),r=`${this.host._space}|${this.host._cfgEpoch}|${i.length}`,l=this.host._wallUnionCache?.key===r?this.host._wallUnionCache.value:null,h=this._rszSnapshot();this.host._resize.begin({pointerId:t.pointerId,start:[n[0],n[1]],roomId:e,plan:a,options:this._rszOptsFor(a.a,a.b),rooms:i,openings:this._rszOpenings(),snapshotIdentity:h,before:JSON.parse(h),wallUnionBefore:l})}_rszReasonText(t){return this.host._t(`resize.disabled.${t}`)}_rszDisabledActivate(t,e){t.stopPropagation(),t.preventDefault(),this.host._showToast(this._rszReasonText(e))}_rszDisabledKey(t,e){"Enter"!==t.key&&" "!==t.key||this._rszDisabledActivate(t,e)}_rszMove(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this._svgPoint(t),s=this.host._resize.move({pointerId:t.pointerId,point:[e[0],e[1]],step:this.host._gridPitch,snap:t=>{const e=this._snap(t);return[e[0],e[1]]},project:(t,e,s,i,o)=>this._rszProjectPreview(t,e,s,i,o),publish:(t,e)=>this._rszAcceptPreview(t,e),measure:(t,e)=>this._rszEdgeLabels(t,e)});if("rejected"===s.kind)return s.notify&&this.host._showToast(this.host._rszLimitViolation?`${this.host._t("resize.limit_stopped")} — `+this._junctionLimitLabel(this.host._rszLimitViolation):this.host._t("resize.preview_failed")),void this.host.requestUpdate();"accepted"===s.kind&&this.host.requestUpdate()}_rszUp(t){if(!this.host._resize.ownsPointer(t.pointerId))return;t.stopPropagation();const e=this.host._resize.finish({pointerId:t.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:t=>this._rszCandidateRenderable(t)});if("no-op"===e.kind)return this.host._cfgEpoch++,void this.host.requestUpdate();if("rejected"===e.kind)return this.host._cfgEpoch++,this.host._showToast(this.host._t("resize.commit_failed")),void this.host.requestUpdate();const s=e.preview,i=this.host._serverCfg?.spaces.find(t=>t.id===s.space);i&&(i.rooms=s.sp.rooms,i.openings=s.sp.openings,Array.isArray(s.sp.walls)&&(s.sp.walls.length?i.walls=s.sp.walls:delete i.walls),Array.isArray(s.sp.wall_segments)&&(i.wall_segments=s.sp.wall_segments)),this.host._suppressClick=!0,setTimeout(()=>this.host._suppressClick=!1,0),this._commitPhysicalGeometry(this.host._t("history.resize_room"),e.before),this.host.requestUpdate()}_rszCancelDrag(t){const e=this.host._resize.cancel(this._rszSnapshot(),t);if("no-op"!==e.kind){if(this.host._cfgEpoch++,e.restoreWallUnion){const t=this.host._spaceModel();if(t){const s=`${this.host._space}|${this.host._cfgEpoch}|${t.rooms.length}`,i={key:s,value:e.restoreWallUnion};rh(this.host._wallUnionPool,s,i,8),this.host._wallUnionCache=i}}this.host.requestUpdate()}}_rszPointerCancel(t){this.host._resize.ownsPointer(t.pointerId)&&(t.stopPropagation(),this._rszCancelDrag(t.pointerId))}_rszEdgeLabels(t,e,s=this.host._resize.rooms){const i=s,o=[],a=t.polys[e.roomId]||i?.find(t=>t.id===e.roomId).poly;if(!a||!i)return o;const n=a.length,r=this._rszInnerSpanCms(e.roomId,a,t.polys);for(const t of function(t,e){const s=t.length;return[(e-1+s)%s,(e+1)%s]}(a,e.edge)){const e=a[t],s=a[(t+1)%n],i=r?.[t];o.push({kind:"length",x:(e[0]+s[0])/2,y:(e[1]+s[1])/2,text:null==i?this.host._fmtLen(e,s):Se(i,"mi"===this.host.hass?.config?.unit_system?.length),edge:{a:[e[0],e[1]],b:[s[0],s[1]]}})}const l="mi"===this.host.hass?.config?.unit_system?.length,h=e.roomIds,c=this.host._spaceWalls,d=this.host._physicalBodiesR(),p=this.host._baseVb(),u=this.host._view&&this.host._view.w>0&&this.host._view.h>0?this.host._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this.host._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this.host._spaceModel(),b=this.host._config?.icon_size??2.5,v=f?Me(b>8?2.5:b,f,u.w,this.host._kiosk?this.host._kioskScale.icon:1)*g.stageWidth/100:24,y=Math.max(10,.77*v),$=this.host._t("room.settings_short"),w=y*(1.457+.66*Math.max(1,$.length)*.42);for(const s of h){const a=t.polys[s]||i.find(t=>t.id===s).poly,n=c.length&&f&&this.host._innerRoomContour(f,s)||a,r=d.length?De(Ce(n,d))*Math.pow(this.host._cellCm/this.host._gridPitch,2)/1e4:Pe(n,this.host._gridPitch,this.host._cellCm),h=Ie(r,l),p=Pa({poly:a,edge:e.edgeByRoom[s],text:h,view:g,gearCenter:Te(a),gearWidthPx:w,gearHeightPx:y});o.push({kind:"area",roomId:s,x:p.anchor[0],y:p.anchor[1],text:h,placement:p})}return o}_rszInnerSpanCms(t,e,s){const i=Object.keys(s).length?Object.entries(s).map(([t,e])=>({id:t,poly:e})):this.host._spaceModel()?.rooms;if(!i?.length)return null;const o=this.host._spaceWalls;if(!o.length)return null;const a=this.host._openCuts(),n=Re(i,t,o,a,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh);if(!n||n.length!==e.length)return null;const r=this.host._cellCm/this.host._gridPitch;return e.map((t,s)=>ze(e,s,n)*r)}_renderResizeMeasurements(){const t=this.host._resize.liveLabels;if(!t?.length)return d;const e=t.filter(t=>"length"===t.kind),s=t.filter(t=>"area"===t.kind);return Rt``}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),s=e/2,i=t=>t.toFixed(1),o=`M ${i(-.7*s)} 0 H ${i(.7*s)} M 0 ${i(-.22*s)} V ${i(-s)} M ${i(-.32*s)} ${i(-.6*s)} L 0 ${i(-s)} L ${i(.32*s)} ${i(-.6*s)} M 0 ${i(.22*s)} V ${i(s)} M ${i(-.32*s)} ${i(.6*s)} L 0 ${i(s)} L ${i(.32*s)} ${i(.6*s)}`,a=[],n=this._rszRooms(),r=this.host._resize.snapshotIdentity||this._rszSnapshot();for(const t of n)for(let s=0;sthis._rszEdgeDown(e,t.id,s)} - @pointermove=${t=>this._rszMove(t)} - @pointerup=${t=>this._rszUp(t)} - @pointercancel=${t=>this._rszPointerCancel(t)} - @lostpointercapture=${t=>this._rszPointerCancel(t)} - @click=${u?t=>this._rszDisabledActivate(t,p.reason):null} - @keydown=${u?t=>this._rszDisabledKey(t,p.reason):null}> - ${_} - `),a.push(Rt``)}return Rt`${a}`}_partitionOpeningCuts(t=this.host._spaceModel(),e=()=>!0){if(!t)return[];const s=this.host._curSpaceCfg?.id===t.id?this.host._curSpaceCfg:null,i=Ae(s,t,this.host._cellCm,this.host._gridPitch,lh);return Fe(i,e)}_decorSnapGeometry(t){const e=this.host._spaceModel();if(!e)return{points:[],segments:[]};const s=t||"",i=this.host._decorSnapCache;if(i&&i.epoch===this.host._cfgEpoch&&i.space===this.host._space&&i.height===this.host._decorH&&i.exclude===s)return i.geometry;const o=[];for(const e of this.host._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*lh,e.y1*this.host._decorH],s=[e.x2*lh,e.y2*this.host._decorH];o.push({points:[t,s,[(t[0]+s[0])/2,(t[1]+s[1])/2]],segments:[{a:t,b:s}]})}else if("text"===e.kind)o.push({points:[[e.x*lh,e.y*this.host._decorH]],segments:[]});else{const t=this.host._decorBoxOf(e);t&&o.push(Ne(t))}for(const t of e.rooms){const e=V(t);e?.length&&o.push({points:e.flatMap((t,s)=>{const i=e[(s+1)%e.length];return[t,[(t[0]+i[0])/2,(t[1]+i[1])/2]]}),segments:e.map((t,s)=>({a:t,b:e[(s+1)%e.length]}))})}const a=He(o);return this.host._decorSnapCache={epoch:this.host._cfgEpoch,space:this.host._space,height:this.host._decorH,exclude:s,geometry:a},a}_decorSnap(t,e="mouse",s){const i=this.host._stageEl,o=this.host._viewOr(this.host._baseVb()),a="touch"===e||"pen"===e?14:8,n=i?o.w/Math.max(1,i.clientWidth)*a:this.host._gridPitch;return Le(t,this._decorSnapGeometry(s),n,t=>this._snap(t)).point}_replaceDecor(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>s.id===t?{...s,...e}:s),this.host.requestUpdate())}_cancelDecorGesture(){const t=this.host._decorMove?.before||this.host._dtDrag?.before||this.host._bdDrag?.before,e=t&&this.host._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const s=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=s(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,s(t.plan_transform||{})),this.host._cfgEpoch++}this.host._decorMove=null,this.host._dtDrag=null,this.host._bdDrag=null,this.host.requestUpdate()}_decorPointerDown(t){const e=this.host._decorTool;if("mouse"===t.pointerType&&0!==t.button&&"select"!==e&&"erase"!==e)return!1;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const s=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorDraft={kind:e,a:s,b:s,pid:t.pointerId},ch(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this.host._decorTextDialog={x:A(e[0]/lh),y:A(e[1]/this.host._decorH),text:"",color:this.host._decorStyle.color,opacity:this.host._decorStyle.opacity,angle:"0",sizeCm:Ee(St,this.host._cellCm,this.host._gridPitch)},this.host._decorTextSelection={start:0,end:0},!0}if("furniture"===e||"image"===e){if("furniture"===e?!this.host._furnPalette:!this.host._decorImagePalette)return!1;t.preventDefault();const s=t.pointerType||"mouse";if("mouse"===s)return"furniture"===e?this._furnPlace(this._svgPoint(t),t.shiftKey,s):this._decorImagePlace(this._svgPoint(t),s),!0;const i=this.host._furnTouchPending;return i&&i.pid!==t.pointerId?(i.cancelled=!0,this.host._furnPreviewInput=null,!0):(this.host._furnTouchPending={pid:t.pointerId,sx:t.clientX,sy:t.clientY,pointerType:s,cancelled:!1},this.host._furnPreviewInput=null,ch(t),!0)}if("select"===e&&(this.host._decorSel=null),this.host._bdMovable){const e=this.host._bdRect,s=this._svgPoint(t),i=e.x+e.w/2,o=e.y+e.h/2,a=-Oe(e.angle)*Math.PI/180,n=s[0]-i,r=s[1]-o,l=[i+n*Math.cos(a)-r*Math.sin(a),o+n*Math.sin(a)+r*Math.cos(a)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this.host._decorDraft;if(this.host._decorDraft=null,!t)return;const e=.5*this.host._gridPitch;if(!Be(t.kind,t.a,t.b,e))return;const s=lh,i=this.host._decorH,o=this.host._decorStyle,a=this._geometrySnapshot(),n="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r=A;let l;if("line"===t.kind)l={id:n,kind:"line",x1:r(t.a[0]/s),y1:r(t.a[1]/i),x2:r(t.b[0]/s),y2:r(t.b[1]/i),...We(o,!1)};else{const e=r(Math.min(t.a[0],t.b[0])/s),a=r(Math.min(t.a[1],t.b[1])/i),h=Math.abs(t.b[0]-t.a[0])/s,c=Math.abs(t.b[1]-t.a[1])/i;l={id:n,kind:t.kind,x:e,y:a,w:h,h:c,...We(o,!0)}}this.host._curSpaceCfg.decor=[...this.host._decorList,l],this.host._decorSel=n,this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this.host._mode)return;const s=this.host._decorTool;if("text"===s){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==s&&"erase"!==s||(t.stopPropagation(),t.preventDefault(),"erase"!==s?(this.host._decorSel=e.id,this.host._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},ch(t)):this.host._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this.host._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const s=this._svgPoint(t),i=e.orig,o=("line"===i.kind?i.x1:i.x)*lh,a=("line"===i.kind?i.y1:i.y)*this.host._decorH,n=this._decorSnap([o+(s[0]-e.start[0]),a+(s[1]-e.start[1])],t.pointerType,e.id);let r=(n[0]-o)/lh,l=(n[1]-a)/this.host._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=Ht;r=Math.max(-_-c,Math.min(_-c-p,r)),l=Math.max(-_-d,Math.min(_-d-u,l)),(r||l)&&(e.moved=!0);this.host._curSpaceCfg.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s=e.orig;return"line"===t.kind?{...t,x1:s.x1+r,y1:s.y1+l,x2:s.x2+r,y2:s.y2+l}:{...t,x:s.x+r,y:s.y+l}}),this.host.requestUpdate()}_decorShapeDbl(t,e){"decor"===this.host._mode&&"select"===this.host._decorTool&&(t.preventDefault(),t.stopPropagation(),this.host._decorMove=null,this.host._decorSel=e.id,this._openDecorProperties(e))}_openDecorProperties(t){if("decor"!==this.host._mode||"select"!==this.host._decorTool)return;if("text"===t.kind)return void this._decorOpenText(t);if(!["line","rect","ellipse","furniture","image"].includes(t.kind))return;"image"===t.kind&&this._decorImageCatalogLoad();const e=this.host._decorResolvedStyle(t),s="line"===t.kind?t:null,i=this.host._decorBoxOf(t);this.host._decorShapeDialog={id:t.id,kind:t.kind,color:e.color,opacity:e.opacity,widthCm:e.widthCm,angle:this.host._angleField(Oe(s?je([s.x1*lh,s.y1*this.host._decorH],[s.x2*lh,s.y2*this.host._decorH]):t.angle)),...s?{lengthCm:Ee(Math.hypot((s.x2-s.x1)*lh,(s.y2-s.y1)*this.host._decorH),this.host._cellCm,this.host._gridPitch),lineStyle:"dashed"===s.line_style?"dashed":"solid"}:{},...i?{sizeWCm:Ee(i.w,this.host._cellCm,this.host._gridPitch),sizeHCm:Ee(i.h,this.host._cellCm,this.host._gridPitch)}:{},..."furniture"===t.kind?{symbol:t.symbol,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Lt(Ee(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Lt(Ee(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."image"===t.kind?{assetId:t.asset_id,flipH:!!t.flip_h,flipV:!!t.flip_v,sizeWField:Lt(Ee(i.w,this.host._cellCm,this.host._gridPitch),!!t.flip_h,this.host._imperial),sizeHField:Lt(Ee(i.h,this.host._cellCm,this.host._gridPitch),!!t.flip_v,this.host._imperial)}:{},..."rect"===t.kind||"ellipse"===t.kind?{fill:e.fill,fillColor:e.fillColor,fillOpacity:e.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const s=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!Dt(t[1])),i=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||i?"":Ct(t.entity,o),n=!(!String(t.entity??"").trim()||s||a&&!i);if(a&&!n){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+a+e.slice(t+2):`${e}${e?" ":""}${a}`}this.host._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:b(t.color,this.host._decorStyle.color),opacity:Ge(t.opacity,this.host._decorStyle.opacity),angle:this.host._angleField(t.angle),sizeCm:this.host._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:n||void 0},this.host._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this.host._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this.host._decorTextDialog;if(!e)return;const s=Ct(e.pickerEntity,t);if(!s)return;const i=e.text,o=Math.max(0,Math.min(i.length,this.host._decorTextSelection.start)),a=Math.max(o,Math.min(i.length,this.host._decorTextSelection.end));if(i.length-(a-o)+s.length>200)return;const n=i.slice(0,o)+s+i.slice(a),r=o+s.length;this.host._decorTextSelection={start:r,end:r},this.host._decorTextDialog={...e,text:n,preserveLegacy:void 0},this.host.updateComplete.then(()=>{const t=this.host.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(r,r))})}_decorSaveText(){const t=this.host._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this.host._decorTextDialog=null);const s=this._geometrySnapshot(),i=this.host._curSpaceCfg,o={color:t.color,opacity:Ge(t.opacity),size_cm:Number(Math.max(.1,Math.min(oh,t.sizeCm)).toFixed(4)),...Oe(t.angle)?{angle:Oe(t.angle)}:{}};if(t.id)i.decor=this.host._decorList.map(s=>{if(s.id!==t.id)return s;if("text"!==s.kind)return s;if(t.preserveLegacy){const{angle:t,size:i,scale:a,...n}=s;return{...n,text:e,...o}}const{entity:i,attr:a,unit:n,...r}=s,{angle:l,size:h,scale:c,...d}=r;return{...d,text:e,...o}});else{const s="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);i.decor=[...this.host._decorList,{id:s,kind:"text",x:t.x,y:t.y,text:e,...o}],this.host._decorSel=s}this.host._decorTextDialog=null,this._recordGeometry(this.host._t(t.id?"history.decor_edit":"history.decor_add"),s),this._saveConfig(),this.host.requestUpdate()}_decorSaveShape(){const t=this.host._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),s={color:t.color,opacity:Ge(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Ge(t.fillOpacity,.25)},i=this.host._curSpaceCfg,o="furniture"===t.kind||"image"===t.kind?Nt(t.sizeWField,this.host._imperial,Ht*this.host._cellCm):null,a="furniture"===t.kind||"image"===t.kind?Nt(t.sizeHField,this.host._imperial,Ht*this.host._cellCm):null;("furniture"!==t.kind&&"image"!==t.kind||null!==o&&null!==a)&&(i.decor=this.host._decorList.map(e=>{if(e.id!==t.id)return e;const i="rect"===t.kind||"ellipse"===t.kind,n=We(s,i);if("line"===e.kind){const s=(e.x1+e.x2)/2*lh,i=(e.y1+e.y2)/2*this.host._decorH,o=Tt(Math.max(.1,Number(t.lengthCm)||.1),this.host._cellCm,this.host._gridPitch),a=Oe(t.angle)*Math.PI/180,r=Math.cos(a)*o/2,l=Math.sin(a)*o/2,h=this._snap([s-r,i-l]),c=this._snap([s+r,i+l]),{width:d,line_style:p,...u}=e;return{...u,...n,x1:A(h[0]/lh),y1:A(h[1]/this.host._decorH),x2:A(c[0]/lh),y2:A(c[1]/this.host._decorH),..."dashed"===t.lineStyle?{line_style:"dashed"}:{}}}if("furniture"===e.kind){const s=e.w*lh,i=e.h*this.host._decorH,r=Tt(Math.abs(o),this.host._cellCm,this.host._gridPitch),l=Tt(Math.abs(a),this.host._cellCm,this.host._gridPitch),h=e.x*lh+s/2,c=e.y*this.host._decorH+i/2,d=Oe(t.angle),{width:p,angle:u,flip_h:_,flip_v:m,...g}=e;return{...g,...n,x:A((h-r/2)/lh),y:A((c-l/2)/this.host._decorH),w:r/lh,h:l/this.host._decorH,...t.symbol?{symbol:t.symbol}:{},...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...d?{angle:d}:{}}}if("image"===e.kind){const s=e.w*lh,i=e.h*this.host._decorH,n=Tt(Math.abs(o),this.host._cellCm,this.host._gridPitch),r=Tt(Math.abs(a),this.host._cellCm,this.host._gridPitch),l=e.x*lh+s/2,h=e.y*this.host._decorH+i/2,c=Oe(t.angle);return{id:e.id,kind:"image",asset_id:t.assetId||e.asset_id,x:A((l-n/2)/lh),y:A((h-r/2)/this.host._decorH),w:n/lh,h:r/this.host._decorH,opacity:Ge(t.opacity,1),...o<0?{flip_h:!0}:{},...a<0?{flip_v:!0}:{},...c?{angle:c}:{}}}if("rect"===e.kind||"ellipse"===e.kind){const s=e.w*lh,i=e.h*this.host._decorH,o=Math.max(this.host._gridPitch,oe(Tt(Number(t.sizeWCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),a=Math.max(this.host._gridPitch,oe(Tt(Number(t.sizeHCm),this.host._cellCm,this.host._gridPitch),this.host._gridPitch)),r=e.x*lh+s/2,l=e.y*this.host._decorH+i/2,h=Oe(t.angle),c=qe({x:r-o/2,y:l-a/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...n,x:A(c[0]/lh),y:A(c[1]/this.host._decorH),w:o/lh,h:a/this.host._decorH,...h?{angle:h}:{}}}return e}),"image"!==t.kind&&this._updateDecorStyle({...s,fill:"rect"===t.kind||"ellipse"===t.kind?s.fill:this.host._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?s.fillColor:this.host._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?s.fillOpacity:this.host._decorStyle.fillOpacity}),this.host._decorShapeDialog=null,this._recordGeometry(this.host._t("history.decor_edit"),e),this._saveConfig(),this.host.requestUpdate())}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*lh,(t.y1+t.y2)/2*this.host._decorH]:"furniture"===t.kind||"image"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*lh,(t.y+t.h/2)*this.host._decorH]:[t.x*lh,t.y*this.host._decorH]}_dtApply(t,e){const s=this.host._curSpaceCfg;s&&(s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const i={...s};"text"===s.kind&&void 0!==e.textSizeCm&&(delete i.size,delete i.scale);const o={...i};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(oh,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this.host._cfgEpoch++,this.host.requestUpdate())}_dtStart(t,e,s,i){const o=this.host._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[a,n]=this._dtPivot(o),r=this._svgPoint(t),l=this.host._decorBoxOf(o);this.host._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:a,ay:n,r0:Math.hypot(r[0]-a,r[1]-n),a0:180*Math.atan2(r[1]-n,r[0]-a)/Math.PI,textSizeCm0:"text"===o.kind?this.host._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:s?.[0],sgy:s?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:i,moved:!1},ch(t)}_dtMove(t){const e=this.host._dtDrag;if(!e)return;const s=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const i=this._decorSnap(s,t.pointerType,e.id),o=A(i[0]/lh),a=A(i[1]/this.host._decorH),n=e.origShape,r=0===e.lineEnd?n.x1:n.x2,l=0===e.lineEnd?n.y1:n.y2;return(Math.abs(o-r)>1e-9||Math.abs(a-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:a}:{x2:o,y2:a})}if("scale"===e.kind&&e.orig){if("furniture"===e.origShape.kind||"image"===e.origShape.kind){const i=Ue({...e.orig,flip_h:e.origShape.flip_h,flip_v:e.origShape.flip_v},e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,Tt(.1,this.host._cellCm,this.host._gridPitch)),o=Math.abs(i.x-e.orig.x)>1e-9||Math.abs(i.y-e.orig.y)>1e-9||Math.abs(i.w-e.orig.w)>1e-9||Math.abs(i.h-e.orig.h)>1e-9||!!i.flip_h!=!!e.origShape.flip_h||!!i.flip_v!=!!e.origShape.flip_v;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyFurnitureBox(e.id,i)}const i=Ke(e.orig,e.sgx??1,e.sgy??1,s[0],s[1],!t.shiftKey,this.host._gridPitch,this.host._gridPitch),o=Math.abs(i.x-e.orig.x)>1e-6||Math.abs(i.y-e.orig.y)>1e-6||Math.abs(i.w-e.orig.w)>1e-6||Math.abs(i.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,i)}if("scale"===e.kind){const t=Math.hypot(s[0]-e.ax,s[1]-e.ay);if(e.r0<1e-6)return;const i=Math.max(.1,Math.min(oh,e.textSizeCm0*(t/e.r0))),o=Math.abs(i-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:i})}const i=180*Math.atan2(s[1]-e.ay,s[0]-e.ax)/Math.PI;let o;"furniture"===e.origShape.kind||"image"===e.origShape.kind?o=Ve(e.angle0,e.a0,i,t.shiftKey):(o=e.angle0+(i-e.a0),t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360));const a=Math.abs(o-e.angle0)>1e-6;(a||e.moved)&&(e.moved||=a,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this.host._dtDrag;this.host._dtDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.decor_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_dtMeasure(){const t=this.host._dtSel;if(!t)return void(this.host._dtBox&&(this.host._dtBox=null,this.host.requestUpdate()));let e;if("line"===t.kind){const s=t.x1*lh,i=t.y1*this.host._decorH,o=t.x2*lh,a=t.y2*this.host._decorH;e={id:t.id,x:Math.min(s,o),y:Math.min(i,a),w:Math.abs(o-s),h:Math.abs(a-i)}}else if("furniture"===t.kind||"image"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*lh,y:t.y*this.host._decorH,w:t.w*lh,h:t.h*this.host._decorH};else{const s=this.host.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!s||"function"!=typeof s.getBBox)return;let i;try{i=s.getBBox()}catch{return}if(!i||!i.width&&!i.height)return;e={id:t.id,x:i.x,y:i.y,w:i.width,h:i.height}}const s=this.host._dtBox;s&&s.id===e.id&&Math.abs(s.x-e.x)<.01&&Math.abs(s.y-e.y)<.01&&Math.abs(s.w-e.w)<.01&&Math.abs(s.h-e.h)<.01||(this.host._dtBox=e,this.host.requestUpdate())}_deleteDecor(t){if(!this.host._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this.host._curSpaceCfg.decor=this.host._decorList.filter(e=>e.id!==t),this.host._decorSel===t&&(this.host._decorSel=null),this._recordGeometry(this.host._t("history.decor_delete"),e),this._saveConfig(),this.host.requestUpdate()}_decorDeleteSel(){this.host._decorSel&&this._deleteDecor(this.host._decorSel)}_confirmDecorErase(){const t=this.host._decorEraseConfirm;this.host._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}_furnFieldValue(t){return Math.round(100*(this.host._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return Je(this.host._imperial?30.48*t:100*t)}_furnShiftAttach(){this._furnShiftAttached||(this._furnShiftAttached=!0,window.addEventListener("keydown",this._furnShiftListener),window.addEventListener("keyup",this._furnShiftListener))}_furnShiftDetach(){this._furnShiftAttached&&(this._furnShiftAttached=!1,window.removeEventListener("keydown",this._furnShiftListener),window.removeEventListener("keyup",this._furnShiftListener))}_furnPick(t){const e=Ye(t);this.host._furnPalette={symbol:t,w:e.w,h:e.h},this._furnShiftAttach()}get _furnWalls(){return function(t){const e=t._spaceModel();if(!e)return[];const s=[e.id,t._cfgEpoch,t._cellCm,t._gridPitch,t._wallKeyPitch].join("|"),i=Na.get(t);if(i?.key===s)return i.value;const o=[...Aa(e.rooms,t._spaceWalls,t._openCuts(),t._wallKeyPitch,t._cellCm,t._gridPitch,E),...Fa(t._rawPhysicalBodiesR())];return Na.set(t,{key:s,value:o}),o}(this.host)}_resolveFurniturePlacement(t,e=!1,s="mouse"){const i=this.host._furnPalette,o=this.host._curSpaceCfg;if(!i||!o)return null;const a=this.host._decorH,n=this._decorSnap(t,s);return function(t){const{symbol:e,widthCm:s,depthCm:i,point:o,canvasW:a,canvasH:n,cellCm:r,gridPitch:l,walls:h,wallReach:c,free:d=!1,intentPoint:p=o,preferredNormal:u}=t;if(!(z(e)&&n>0&&Number.isFinite(o[0])&&Number.isFinite(o[1])))return null;const _=Number.isFinite(p[0])&&Number.isFinite(p[1])?p:o,m=F(N(s,r,l,a)),g=F(N(i,r,l,a));let f=o[0],b=o[1],v=0;const y=d?null:Ta(f,b,g*n,h,c,l,_,u);return y&&(f=y.cx,b=y.cy,v=y.angle),{symbol:e,x:A(f/a-m/2),y:A(b/n-g/2),w:m,h:g,angle:Number(v.toFixed(2))}}({symbol:i.symbol,widthCm:i.w,depthCm:i.h,point:[n[0],n[1]],canvasW:1e3,canvasH:a,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,walls:this._furnWalls,wallReach:6*this.host._gridPitch,intentPoint:[t[0],t[1]],free:e})}_furniturePreviewPlacement(){const t=this.host._furnPreviewInput;return t&&"decor"===this.host._mode&&"furniture"===this.host._decorTool&&this.host._furnPalette&&this.host._pointerModality.hoverEnabled?this._resolveFurniturePlacement(t.raw,t.free,"mouse"):null}_renderFurniturePlacementPreview(t){const e=this._furniturePreviewPlacement();if(!e)return d;const s=z(e.symbol);if(!s)return d;const i=Ze(e,lh,this.host._decorH,s.viewW,s.viewH),o=this.host._decorStyle,a=Xe(Tt(o.widthCm,this.host._cellCm,this.host._gridPitch),t);return Rt``}_furnLive(){const t=this.host._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this.host._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const s=e.w*lh,i=e.h*this.host._decorH,o=Qe(e.x*lh,e.y*this.host._decorH,s,i,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],n=a(o[0],o[1]),r=a(o[0],o[3]);return[{x:n[0],y:n[1],text:this.host._fmtLen([0,0],[s,0])},{x:r[0],y:r[1],text:this.host._fmtLen([0,0],[0,i])}]}_furnPlace(t,e=!1,s="mouse"){const i=this._resolveFurniturePlacement(t,e,s);if(!i)return;const o=this.host._curSpaceCfg;if(!o)return;const a=this._geometrySnapshot(),n="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),r={id:n,kind:"furniture",symbol:i.symbol,x:i.x,y:i.y,w:i.w,h:i.h,...We(this.host._decorStyle,!1)};i.angle&&(r.angle=i.angle),o.decor=[...this.host._decorList,r],this.host._decorSel=n,this.host._decorTool="select",this.host._furnPalette=null,this.host._furnCategory=null,this._furnShiftDetach(),this._clearFurniturePreview(),this._recordGeometry(this.host._t("history.decor_add"),a),this._saveConfig(),this.host.requestUpdate()}_clearFurniturePreview(){this.host._furnPreviewInput=null,this.host._furnTouchPending=null}_furnPointerMove(t,e){const s=this.host._furnTouchPending;if(s?.pid===t.pointerId)return Math.abs(t.clientX-s.sx)+Math.abs(t.clientY-s.sy)>8&&(s.cancelled=!0),this.host._furnPreviewInput=null,!0;const i="furniture"===this.host._decorTool?!!this.host._furnPalette:"image"===this.host._decorTool&&!!this.host._decorImagePalette;if("decor"!==this.host._mode||!i)return this._clearFurniturePreview(),!1;if("mouse"!==t.pointerType||!e)return this.host._furnPreviewInput=null,!1;const o=this._svgPoint(t);return this.host._furnPreviewInput={raw:[o[0],o[1]],free:t.shiftKey},!0}_furnPointerLeave(t){this.host._furnTouchPending?.pid===t.pointerId&&(this.host._furnTouchPending.cancelled=!0),this.host._furnPreviewInput=null}_furnPointerUp(t){const e=this.host._furnTouchPending;return!(!e||e.pid!==t.pointerId)&&(this.host._furnTouchPending=null,!e.cancelled&&"decor"===this.host._mode&&("furniture"===this.host._decorTool&&this.host._furnPalette||"image"===this.host._decorTool&&this.host._decorImagePalette)&&("furniture"===this.host._decorTool?this._furnPlace(this._svgPoint(t),t.shiftKey,e.pointerType):this._decorImagePlace(this._svgPoint(t),e.pointerType)),!0)}_furnMoveUpdate(t){const e=this.host._decorMove;if("furniture"!==e.orig.kind)return;const s=e.orig,i=this.host._curSpaceCfg;if(!i)return;const o=lh,a=this.host._decorH,n=this._svgPoint(t),r=(s.x+s.w/2)*o+(n[0]-e.start[0]),l=(s.y+s.h/2)*a+(n[1]-e.start[1]);let h,c,d=Number(s.angle)||0;const p=d*Math.PI/180,u=[-Math.sin(p),Math.cos(p)],_=t.shiftKey?null:Ta(r,l,s.h*a,this._furnWalls,6*this.host._gridPitch,this.host._gridPitch,[r,l],u);if(_)h=_.cx/o-s.w/2,c=_.cy/a-s.h/2,d=_.angle;else{const i=this._decorSnap([r-s.w/2*o,l-s.h/2*a],t.pointerType,e.id);h=i[0]/o,c=i[1]/a}h=A(h),c=A(c),(Math.abs(h-s.x)>1e-9||Math.abs(c-s.y)>1e-9||Math.abs(d-(Number(s.angle)||0))>1e-9)&&(e.moved=!0),i.decor=this.host._decorList.map(t=>{if(t.id!==e.id)return t;const s={...t,x:h,y:c};return d?s.angle=Number(d.toFixed(2)):delete s.angle,s}),this.host.requestUpdate()}_decorApplyBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=lh,o=this.host._decorH;s.decor=this.host._decorList.map(s=>{if(s.id!==t)return s;const a=qe(e,s.angle,t=>this._snap(t));return{...s,x:A(a[0]/i),y:A(a[1]/o),w:Math.max(this.host._gridPitch/i,Math.min(2*Ht,e.w/i)),h:Math.max(this.host._gridPitch/o,Math.min(2*Ht,e.h/o))}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorApplyFurnitureBox(t,e){const s=this.host._curSpaceCfg;if(!s)return;const i=lh,o=this.host._decorH,a=Tt(.1,this.host._cellCm,this.host._gridPitch);s.decor=this.host._decorList.map(s=>{if(s.id!==t||"furniture"!==s.kind&&"image"!==s.kind)return s;const{flip_h:n,flip_v:r,...l}=s;return{...l,x:A(e.x/i),y:A(e.y/o),w:Math.max(a/i,Math.min(2*Ht,e.w/i)),h:Math.max(a/o,Math.min(2*Ht,e.h/o)),...e.flip_h?{flip_h:!0}:{},...e.flip_v?{flip_v:!0}:{}}}),this.host._cfgEpoch++,this.host.requestUpdate()}_decorImagePlace(t,e="mouse"){this._decorImages.place(t,e)}_renderDecorImagePlacementPreview(){return this._decorImages.renderPlacementPreview()}_renderMissingDecorImage(t,e,s,i,o,a,n,r,l){return this._decorImages.renderMissing(t,e,s,i,o,a,n,r,l)}async _decorImageUpload(t,e=!1){await this._decorImages.uploadFromInput(t,e)}async _uploadDecorImage(t,e,s){await this._decorImages.upload(t,e,s)}async _decorImageDelete(t){await this._decorImages.delete(t)}async _decorImageCatalogLoad(){await this._decorImages.loadCatalog()}_renderDecorImagePalette(){return this._decorImages.renderImagePalette()}_renderFurnPalette(){return this._decorImages.renderFurniturePalette()}_openBackdropDialog(t){if(!this.host._bdMovable||!this.host._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this.host._bdDrag=null;const e=this.host._bdRect;this.host._backdropDialog={widthCm:Ee(e.w,this.host._cellCm,this.host._gridPitch),heightCm:Ee(e.h,this.host._cellCm,this.host._gridPitch),angle:this.host._angleField(e.angle)}}_saveBackdropDialog(){const t=this.host._backdropDialog,e=this.host._bdBase,s=this.host._bdRect;if(!t||!e||!s)return;const i=Oe(t.angle),o=this._geometrySnapshot(),a=Math.min(e.w*xt,Math.max(e.w*kt,oe(Tt(t.widthCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),n=Math.min(e.h*xt,Math.max(e.h*kt,oe(Tt(t.heightCm,this.host._cellCm,this.host._gridPitch),this.host._gridPitch))),r=s.x+s.w/2,l=s.y+s.h/2,h=qe({x:r-a/2,y:l-n/2},i,t=>this._snap(t));this._bdApply((h[0]-e.x)/lh,(h[1]-e.y)/lh,a/e.w,n/e.h,i),this.host._backdropDialog=null,this._recordGeometry(this.host._t("history.backdrop_transform"),o),this._saveConfig()}_bdApply(t,e,s,i,o){const a=this.host._curSpaceCfg;if(!a)return;a.plan_x=Number(A(t).toFixed(6)),a.plan_y=Number(A(e).toFixed(6)),delete a.plan_scale,a.plan_scale_x=Number(Math.min(xt,Math.max(kt,s)).toFixed(6)),a.plan_scale_y=Number(Math.min(xt,Math.max(kt,i)).toFixed(6));const n=Oe(o);n?a.plan_angle=Number(n.toFixed(2)):delete a.plan_angle,this.host._cfgEpoch++,this.host.requestUpdate()}_bdStart(t,e,s=!1){const i=this.host._bdBase,o=this.host._bdRect;if(!i||!o)return!1;const a=this._svgPoint(t),n=e?e[0]:0,r=e?e[1]:0,l=n>0?o.x:o.x+o.w,h=r>0?o.y:o.y+o.h;return this.host._bdDrag={kind:s?"rotate":e?"scale":"move",pid:t.pointerId,sx:a[0],sy:a[1],base:i,p0:this.host._bdParams,fx:l,fy:h,sgx:n,sgy:r,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},ch(t),!0}_bdMove(t){const e=this.host._bdDrag;if(!e)return;const s=this._svgPoint(t),i=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,a=this._snap([t+(s[0]-e.sx),o+(s[1]-e.sy)]),n=Math.abs(a[0]-t)>1e-9||Math.abs(a[1]-o)>1e-9;if(!n&&!e.moved)return;return e.moved||=n,void this._bdApply((a[0]-i.x)/lh,(a[1]-i.y)/lh,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const i=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,a=180*Math.atan2(e.sy-o,e.sx-i)/Math.PI;let n=e.p0.angle+(180*Math.atan2(s[1]-o,s[0]-i)/Math.PI-a);t.shiftKey||(n=5*Math.round(n/5)),n=Oe(n);const r=Math.abs(n-e.p0.angle)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,n)}const o=Ke(e.rect0,e.sgx,e.sgy,s[0],s[1],!t.shiftKey,this.host._gridPitch,Math.min(i.w,i.h)*kt),a=o.w/Math.max(1e-9,i.w),n=o.h/Math.max(1e-9,i.h),r=Math.abs(a-e.p0.sx)>1e-9||Math.abs(n-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!r&&!e.moved)return;e.moved||=r;const l=qe(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-i.x)/lh,(l[1]-i.y)/lh,a,n,e.p0.angle)}_bdReset(){const t=this.host._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this.host._bdDrag=null,this._recordGeometry(this.host._t("history.backdrop_transform"),e),this._saveConfig(),this.host._showToast(this.host._t("decor.backdrop_reset_done")),this.host.requestUpdate()}_bdUp(){const t=this.host._bdDrag;this.host._bdDrag=null,t?.moved&&(this._recordGeometry(this.host._t("history.backdrop_transform"),t.before),this._saveConfig()),this.host.requestUpdate()}_renderBackdropFrame(t){const e=this.host._bdRect;if(!this.host._bdActive||!e)return d;const s=.02*Math.max(t.w,t.h),i=s/4,o=e.x+e.w/2,a=e.y+e.h/2,n=Oe(e.angle),r=2.2*s;return Rt` - - - {t.stopPropagation(),t.preventDefault(),this._bdStart(t,void 0,!0)}}> - - ${[[-1,-1,"nwse"],[1,-1,"nesw"],[1,1,"nwse"],[-1,1,"nesw"]].map(([t,o,a])=>{const n=t<0?e.x:e.x+e.w,r=o<0?e.y:e.y+e.h;return Rt`{e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} - `}_renderEditorGroupLauncher(t){return this.host._editorSecondary.renderGroupLauncher(t,this.host._editorToolbarGroups,this.host._editorSecondaryCopy)}_runEditorContext(t,e){return this.host._editorSecondary.runContext(t,this.host._editorSecondaryContextId,e)}_renderEditorGroupModel(t){return this.host._editorSecondary.renderGroupModel(t,this.host._editorSecondaryContextId,this.host._editorSecondaryCopy)}_renderDrawWallControl(){const t="column"===this.host._tool?1:0;return p``}_renderPlanSecondary(){const t=this.host._editorSecondaryContextId,e=this.host._physicalSel;if(e){if(!("partition"===e.kind?!!this.host._curSpaceCfg.partitions?.some(t=>t.id===e.id):"column"===e.kind?!!this.host._curSpaceCfg.wall_columns?.some(t=>t.id===e.id):!!this.host._curSpaceCfg.room_drafts?.some(t=>t.id===e.id)))return null;const s="partition"===e.kind?this.host._t("markup.partition"):"column"===e.kind?this.host._t("markup.column"):this.host._t("markup.add");return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` - - `}}const s="draw"===this.host._tool||"column"===this.host._tool,i="column"===this.host._tool?"markup.hint_column":"resize"===this.host._tool?"markup.hint_resize":"wallthick"===this.host._tool?"markup.hint_wallthick":null,o="draw"===this.host._tool?this.host._t(this.host._path.length?"markup.hint_points":"markup.hint_start",this.host._path.length?{n:this.host._path.length}:void 0):"";if(!s&&!i&&!o)return null;const a="draw"===this.host._tool&&this.host._path.length>0;return{contextId:t,kind:a?"operation":"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("draw"===this.host._tool?"markup.add":"column"===this.host._tool?"markup.column":"resize"===this.host._tool?"markup.resize":"markup.wallthick")}),content:p` - ${s?this._renderDrawWallControl():d} - ${o?p`${o}`:d} - ${i?p`${this.host._t(i)}`:d} - ${"draw"===this.host._tool&&this.host._path.length?p``:d}`}}_renderDecorSecondary(){const t=this.host._editorSecondaryContextId;if("furniture"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.furniture")}),launcherId:"furniture",dismissPolicy:this.host._furnPalette?"stay-open-on-canvas":"outside",dismiss:()=>{"furniture"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._furnPalette=null,this._furnShiftDetach(),this.host._furnCategory=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderFurnPalette()};if("image"===this.host._decorTool)return{contextId:t,kind:"palette",ariaLabel:this.host._t("editor.palette",{tool:this.host._t("decor.image")}),launcherId:"image",dismissPolicy:this.host._decorImagePalette?"stay-open-on-canvas":"outside",dismiss:()=>{"image"===this.host._decorTool&&(this._clearFurniturePreview(),this.host._decorImagePalette=null,this.host._decorTool="select",this.host.requestUpdate())},content:this._renderDecorImagePalette()};const e=this.host._dtSel;if(e){const s=this.host._t(`decor.${e.kind}`);return{contextId:t,kind:"selection",ariaLabel:this.host._t("editor.context_actions",{object:s}),visibleLabel:s,content:p` - - `}}const s="line"===this.host._decorTool||"rect"===this.host._decorTool||"ellipse"===this.host._decorTool,i="rect"===this.host._decorTool||"ellipse"===this.host._decorTool,o="backdrop"===this.host._decorTool&&!!this.host._bdRect;return s||o?{contextId:t,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t(s?`decor.${this.host._decorTool}`:"decor.backdrop")}),content:p` - ${s?p` - this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> - - ${i?p` - this._updateDecorStyle({...this.host._decorStyle,fillColor:t.detail.color,fillOpacity:t.detail.opacity})}>`:d} - `:d} - ${o?p`${this.host._t("decor.backdrop_hint")}`:d}`}:null}_withBackdropReset(t){if(!this.host._bdMoved||this.host._bdDrag||"palette"===t?.kind)return t;const e=this.host._editorSecondaryContextId,s=p``;return t?{...t,kind:"selection"===t.kind?"mixed":t.kind,content:p`${t.content}${s}`}:{contextId:e,kind:"tool",ariaLabel:this.host._t("editor.tool_options",{tool:this.host._t("decor.backdrop")}),content:s}}_renderEditorSecondary(){if(!this.host._editing)return d;const t=this.host._editorSecondary.activeGroup(this.host._editorToolbarGroups),e=t?this._renderEditorGroupModel(t):"plan"===this.host._mode?this._renderPlanSecondary():"decor"===this.host._mode?this._withBackdropReset(this._renderDecorSecondary()):null;return this.host._editorSecondary.render(e,this.host._editorSecondaryDialogBlocked)}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this.host._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],...this.host._haDecorAssetsApi===ts?[["image","mdi:image-plus-outline","decor.image"]]:[],["erase","mdi:eraser","decor.erase"]],e=this.host._geometryHistory.undoName,s=this.host._geometryHistory.redoName;return p`
    -
    - ${t.map(([t,e,s])=>p``)} - ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} - this._updateDecorStyle({...this.host._decorStyle,...t.detail})}> - - - -
    -
    - -
    -
    `}_renderDecorEraseConfirm(){const t=this.host._decorEraseConfirm,e=this.host._t(`decor.${t.kind}`);return p`this.host._decorEraseConfirm=null}> -

    ${this.host._t("confirm.erase_decor",{kind:e})}

    -
    - - - -
    -
    `}_renderDecorTextDialog(){const t=this.host._decorTextDialog,e=(t.pickerEntity||"").trim(),s=e?this.host.hass?.states?.[e]:null;return p`this.host._decorTextDialog=null}> -
    - - ${""} - - this.host._decorTextDialog={...t,...e.detail}}> - -
    this.host._decorTextDialog={...t,sizeCm:this.host._decorTextCm(Number(e.target.value))}} /> - ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    - - this.host._decorTextDialog={...t,angle:e.target.value}} /> - - - this.host._decorTextDialog={...t,pickerEntity:e.target.value}} /> - - ${Object.keys(this.host.hass?.states||{}).map(t=>p``)} - - ${e?p` - - - `:d} -
    -
    - - - -
    -
    `}_renderDecorShapeDialog(){return this._decorImages.renderShapeDialog()}_renderBackdropDialog(){const t=this.host._backdropDialog,e=this.host._t(this.host._imperial?"gs.unit_ft":"gs.unit_m");return p`this.host._backdropDialog=null}> -
    - -
    this.host._backdropDialog={...t,widthCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ×this.host._backdropDialog={...t,heightCm:this.host._decorLargeCm(Number(e.target.value))}} /> - ${e}
    - - this.host._backdropDialog={...t,angle:e.target.value}} /> -
    -
    - - -
    -
    `}_cssPxToRender(t){const e=this.host._stageEl,s=this.host._viewOr(this.host._baseVb());return e?.clientWidth&&e.clientHeight?Math.max(s.w/e.clientWidth,s.h/e.clientHeight)*t:this.host._gridPitch/8*t}_deleteRoomClick(t){const e=this.host._spaceModel();if(!e)return;const s=[...e.rooms].reverse().find(e=>this._pointInRoom(t,e));s?s.id&&(this.host._roomDeleteDialog={roomId:s.id,name:s.name}):this.host._showToast(this.host._t("toast.delete_room_pick"))}_wallThickHit(t){const e=this.host._spaceModel();if(!e)return null;const s=6*this.host._gridPitch,i=this.host._openCuts();let o=null;const a=(t,e,i)=>{e>s||(!o||e100)return void this._showPhysicalRange(100);if("room"!==e.source.kind){const t=this._geometrySnapshot();if("partition"===e.source.kind){const t=(s.partitions||[]).find(t=>t.id===e.source.id);if(!t)return;if(0===n&&Gt(s.openings,{kind:"partition",id:t.id}))return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"));t.cm=n}else{const t=(s.room_drafts||[]).find(t=>t.id===e.source.id),i=t?.segments?.[e.source.segment];if(!i)return;i.cm=n}this.host._wallDialog=null;return this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),t)&&this.host._showToast(this.host._t("toast.wallthick_set")),void this.host.requestUpdate()}const r=this._geometrySnapshot(),l=n>0?n:null;if(0===n){const i=.02*this.host._gridPitch,o=[e.a[0],e.a[1],e.b[0],e.b[1]],a=(s.rooms||[]).find(t=>t.id===e.roomId),n=new Set(t&&a&&Array.isArray(a.wall_ids)?a.wall_ids:(s.wall_segments||[]).filter(t=>{const e=[Number(t.a?.[0])*lh,Number(t.a?.[1])*lh],s=[Number(t.b?.[0])*lh,Number(t.b?.[1])*lh];return G(e,o)<=i&&G(s,o)<=i}).map(t=>String(t.id))),r=(s.openings||[]).some(t=>{if("partition"===t.host?.kind)return!1;if("wall"===t.host?.kind&&n.has(t.host.id))return!0;if(t.host)return!1;const e=[Number(t.x)*lh,Number(t.y)*lh];return e.every(Number.isFinite)&&G(e,o)<=i});if(r)return void this.host._showToast(this.host._t("toast.zero_wall_opening_conflict"))}let h,c=this.host._openCuts();if(n>0&&c.length){const o=[];if(t&&e.roomId){const t=(s.rooms||[]).find(t=>t.id===e.roomId),a=new Set(Array.isArray(t?.wall_ids)?t.wall_ids:[]);for(const t of s.wall_segments||[])a.has(t.id)&&o.push([Number(t.a?.[0])*lh,Number(t.a?.[1])*lh,Number(t.b?.[0])*lh,Number(t.b?.[1])*lh]);if(!o.length){const t=i.rooms.find(t=>t.id===e.roomId),s=t?V(t):null;for(let t=0;s&&t!o.some(e=>G([t[0],t[1]],e)<=a&&G([t[2],t[3]],e)<=a&&G([e[0],e[1]],t)<=a&&G([e[2],e[3]],t)<=a))}h=t&&e.roomId?es(s.walls,i.rooms,e.roomId,l,this.host._wallKeyPitch,c,lh):et(s.walls,e.a,e.b,l,this.host._wallKeyPitch,lh),h=this.host._normalizeWalls(h,c);for(const t of s.wall_segments||[])t.cm=mt(h,t.a,t.b,U,1);h.length?s.walls=h:delete s.walls,this.host._wallDialog=null,this._commitPhysicalGeometry(this.host._t("history.wall_thickness"),r)&&this.host._showToast(this.host._t(0===n?"toast.wallthick_cleared":"toast.wallthick_set")),this.host.requestUpdate()}_wallHatchDefs(t){if(!this.host._spaceWalls.length&&!this.host._physicalBodiesR().length&&!this.host._markup)return Rt``;const e=ss(this.host._cellCm);return Rt` - - - - `}_renderWallThickUi(){const t=this.host._wallThickHover;return t&&t.d?Rt``:Rt``}_renderWallThickDialog(){const t=this.host._wallDialog;return t?p`
    t.stopPropagation()}> -
    - - {this.host._wallDialog={...t,value:e.target.value}}} - @keydown=${t=>{"Enter"===t.key&&(t.preventDefault(),this._wallThickApply(!1))}} /> - ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")} -
    -
    - ${"room"===t.source.kind?p``:d} - - -
    -
    `:p``}_openingAt(t){if(!this.host._openingsR.length)return null;const e=this.host._openingsR.flatMap(e=>{const s=e.angle*Math.PI/180,i=t[0]-e.rx,o=t[1]-e.ry,a=i*Math.cos(s)+o*Math.sin(s);return Math.abs(a)>e.rlen/2+is(12,this.host._cellCm)?[]:[{o:e,localY:-i*Math.sin(s)+o*Math.cos(s)}]});if(!e.length)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i).value;return e.find(({o:t,localY:e})=>{const s="gate"===t.type?!t.flip_v:t.flip_v,i=t.partitionHost||this.host._spaceWalls.length||"gate"===t.type?this.host._openingFace(t,o,!!s):{ox:0,oy:0,cm:0,side:-1},a=os({type:t.type,length:t.rlen,angle:t.angle,amount:this.host._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:i});return Math.abs(e)<=a.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this.host._openingPreset;if("opening"!==this.host._tool||!e)return null;const s=this.host._spaceModel();if(!s)return null;const i=this.host._openCuts(),o=this.host._openingWallIndexFor(s,i),a=`${o.key}|partitions:${this.host._cfgEpoch}`;this.host._openingPlacementIntervalsCache&&this.host._openingPlacementIntervalsCache.key===a||(this.host._openingPlacementIntervalsCache={key:a,value:[...Z(s.rooms,this.host._spaceWalls,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh),...as(s.partitions,this.host._cellCm,this.host._gridPitch)]}),this.host._openingDimensionContextCache&&this.host._openingDimensionContextCache.key===a||(this.host._openingDimensionContextCache={key:a,value:Kl({rooms:s.rooms,walls:this.host._spaceWalls,openCuts:i,partitions:s.partitions,roomOpenings:this.host._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(s),pitch:this.host._wallKeyPitch,cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,coordScale:lh,epsilon:2e-4*this.host._gridPitch})});const n=ns({pointer:[t[0],t[1]],preset:e,geometryRevision:this.host._cfgEpoch,renderedLength:this.host._cmToUnits(e.lengthCm),intervals:this.host._openingPlacementIntervalsCache.value,baseTolerance:1.5*this.host._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this.host._pointerModality.modality||"pen"===this.host._pointerModality.modality?10:6),gridStep:this.host._gridPitch});this.host._openingJambBlockCm=n.jambBlockedTarget?n.jambBlockedTarget.physicalHalfWidth/this.host._gridPitch*this.host._cellCm:null;const r=n.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let h;if(r.host){const t=J({id:"preview",type:r.type,x:r.x/lh,y:r.y/lh,angle:r.angle,length:r.renderedLength/lh,host:r.host},s.partitions,lh,this.host._cellCm,this.host._gridPitch).resolved;h=t?rs(t,l):{ox:0,oy:0,cm:0,side:-1}}else h=ls(o.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const c="mi"===this.host.hass?.config?.unit_system?.length,d=th(r,this.host._openingDimensionContextCache.value).map(t=>({x:t.label[0],y:t.label[1],text:Se(t.distance/this.host._gridPitch*this.host._cellCm,c),dimension:t}));return{...r,face:h,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(t){this._activateMarkupTool("opening"),"opening"===this.host._tool&&(this.host._openingPreset=hs(t,++this.host._openingPresetRevision),this.host._openingHoverCandidate=null,this.host._cursorPt=null)}_clearOpeningPlacement(t){this.host._openingHoverCandidate=null,this.host._openingJambBlockCm=null,t&&(this.host._openingPreset=null,this.host._openingRebindId=null)}_openingClick(t){const e=this.host._spaceModel();if(!e)return;const s=this.host._openingRebindId?null:this._openingAt(t);if(s)return void this._editOpening(s);const i=this.host._openingPreset;if(!i)return;const o=this.host._openingHoverCandidate,a=o&&cs(o,[t[0],t[1]],i.revision,this.host._cfgEpoch)?o:this._resolveOpeningPlacement(t);if(!a){if(null!=this.host._openingJambBlockCm)return void this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Se(this.host._openingJambBlockCm,this.host._imperial)}));const s=1.5*this.host._gridPitch,i=q(t,e.rooms,s);return i&&ds(i.x,i.y,i.angle,this.host._openCuts(),s)?void this.host._showToast(this.host._t("toast.opening_on_zero_wall")):void this.host._showToast(this.host._t("toast.opening_no_wall"))}const n=this.host._openingRebindId?this.host._curSpaceCfg?.openings?.find(t=>t.id===this.host._openingRebindId):null;this.host._openingDialog={...n?{id:n.id,type:n.type,lengthCm:Math.round(n.length*lh/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:n.contact||"",lock:n.lock||"",invert:!!n.invert,flipH:!!n.flip_h,flipV:!!n.flip_v}:{type:a.type,lengthCm:a.lengthCm,contact:"",lock:"",invert:!1,flipH:a.flipH,flipV:a.flipV},...a.host?{host:a.host}:{},x:a.x,y:a.y,angle:a.angle},this.host._openingRebindId=null,this.host._openingHoverCandidate=null,this.host._cursorPt=null}_editOpening(t){this.host._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this.host._gridPitch*this.host._cellCm),lengthTouched:!1,contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,..."partition"===t.host?.kind?{host:{...t.host}}:{},x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this.host._mode&&this.host._spaceModel()&&"resize"!==this.host._tool){t.preventDefault(),t.stopPropagation();try{ch(t)}catch{}this.host._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this.host._opDrag.sx)+Math.abs(t.clientY-this.host._opDrag.sy)<=3)return;const s=this.host._spaceModel();if(!s)return;const i=this._svgPoint(t),o=this.host._curSpaceCfg,a=o?.openings?.find(t=>t.id===e.id);if(!a)return;if("partition"===a.host?.kind){const t=s.partitions.find(t=>t.id===a.host.id);if(!t)return;const e=t.b[0]-t.a[0],o=t.b[1]-t.a[1],n=Math.hypot(e,o);if(!(n>1e-9))return;const r=e/n,l=o/n;if(Math.abs((i[0]-t.a[0])*l-(i[1]-t.a[1])*r)>4*this.host._gridPitch)return;const h=a.length*lh/2+tt(t,this.host._cellCm,this.host._gridPitch);let c=(i[0]-t.a[0])*r+(i[1]-t.a[1])*l;if(c=Math.round(c/this.host._gridPitch)*this.host._gridPitch,c=Math.max(h,Math.min(n-h,c)),n<2*h-1e-9)return;const d=c/n,p=(t.a[0]+r*c)/lh,u=(t.a[1]+l*c)/this.host._spaceH;let _=180*Math.atan2(o,e)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=a.host.t!==d||a.x!==p||a.y!==u||a.angle!==_;return this.host._opDrag.moved=!0,m&&(this.host._opDrag.dirty=!0),a.host={...a.host,t:d},a.x=p,a.y=u,a.angle=_,this.host._opMeasure=null,m&&this.host._cfgEpoch++,void this.host.requestUpdate()}const n=q(i,s.rooms,4*this.host._gridPitch);if(!n)return;this.host._opDrag.moved=!0;const r=this._opRuler(n,a.length*lh);this.host._opMeasure=r.measure;const l=r.x/lh,h=r.y/this.host._spaceH,c=a.x!==l||a.y!==h||a.angle!==n.angle;if(c&&(this.host._opDrag.dirty=!0),a.x=l,a.y=h,a.angle=n.angle,Number(this.host._serverCfg?.model_version||0)>=9){const t=_l(a,o?.wall_segments||[]);t?a.host=t:delete a.host}c&&this.host._cfgEpoch++,this.host.requestUpdate()}_opRuler(t,e){const s=this.host._spaceModel()?.rooms||[],i=this.host._gridPitch/2;let o=t.x,a=t.y,n=ps([o,a],t.angle,e,s,i);if(n&&n.centered&&(o!==n.wallCenter[0]||a!==n.wallCenter[1]))[o,a]=n.wallCenter,n=ps([o,a],t.angle,e,s,i);else if(n){const[r,l]=n.wallA,[h,c]=n.wallB,d=h-r,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this.host._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-r)*d+(a-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=r+_/u*d,a=l+_/u*p,n=ps([o,a],t.angle,e,s,i)||n}}if(!n)return{x:o,y:a,angle:t.angle,measure:null};const r="mi"===this.host.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Se(t/this.host._gridPitch*this.host._cellCm,r)});return{x:o,y:a,angle:t.angle,measure:{labels:[l(n.sideA,n.midA),l(n.sideB,n.midB)],guide:n.centered?{x:n.wallCenter[0],y:n.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this.host._opDrag||this.host._opDrag.id!==e.id)return;const s=this.host._opDrag,i=s.moved;this.host._opMeasure=null,i&&s.dirty&&this._commitPhysicalGeometry(this.host._t("history.move_opening"),s.before),i?window.setTimeout(()=>this.host._opDrag=null,0):this.host._opDrag=null}_opClick(t,e){"plan"===this.host._mode&&"resize"===this.host._tool||(t.stopPropagation(),this.host._opDrag?.moved||"plan"===this.host._mode&&this._editOpening(e))}_saveOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=this._geometrySnapshot(),o=this.host._spaceH,a=(e.openings||[]).find(e=>e.id===t.id),n={...a||{},id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/lh,y:t.y/o,angle:t.angle,length:a&&!t.lengthTouched?a.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/lh,...t.host?{host:{...t.host}}:{}};if("partition"===n.host?.kind){const t=us(a,n)?it(n,s.partitions,lh,this.host._cellCm,this.host._gridPitch):J(n,s.partitions,lh,this.host._cellCm,this.host._gridPitch);if(!t.resolved){const e=s.partitions.find(t=>t.id===n.host.id);if("does-not-fit-jamb"===t.reason&&e){const t=tt(e,this.host._cellCm,this.host._gridPitch);this.host._showToast(this.host._t("opening.partition_jamb_margin",{distance:Se(t/this.host._gridPitch*this.host._cellCm,this.host._imperial)}))}else this.host._showToast(this.host._t("opening.partition_orphan"));return}const i=(e.openings||[]).flatMap(t=>{if(!t.host||t.id===n.id)return[];const e=J(t,s.partitions,lh,this.host._cellCm,this.host._gridPitch).resolved;return e?[e]:[]});if(_s(t.resolved,i))return void this.host._showToast(this.host._t("toast.opening_no_wall"));Object.assign(n,Y(n,t.resolved,lh))}else if(Number(this.host._serverCfg?.model_version||0)>=9){const t=_l(n,e.wall_segments||[]);if(!t)return void this.host._showToast(this.host._t("toast.opening_no_wall"));n.host=t}else delete n.host;"passage"===t.type?(delete n.contact,delete n.lock,delete n.invert,delete n.flip_h,delete n.flip_v):(n.contact=t.contact||null,n.lock=("door"===t.type||"gate"===t.type)&&t.lock||null,n.invert=t.invert||void 0,n.flip_h="gate"!==t.type&&t.flipH||void 0,n.flip_v=t.flipV||void 0),e.openings=e.openings||[];const r=e.openings.findIndex(t=>t.id===n.id);r>=0?e.openings[r]=n:e.openings.push(n),this._commitPhysicalGeometry(this.host._t(t.id?"history.edit_opening":"history.add_opening"),i),this.host._openingDialog=null,this.host.requestUpdate()}_deleteOpening(){const t=this.host._openingDialog,e=this.host._curSpaceCfg;if(!t?.id||!e?.openings)return;const s=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._commitPhysicalGeometry(this.host._t("history.delete_opening"),s),this.host._openingDialog=null,this.host.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.host.hass.states)){if(!this.host._openingEntityAvailable(e))continue;const s=e.split(".")[0];if("binary_sensor"!==s&&"cover"!==s)continue;const i=this.host.hass.states[e],o=["door","window","opening","garage_door","garage"].includes(i?.attributes?.device_class||"");("cover"!==s||o)&&t.push([e,i?.attributes?.friendly_name||e,o?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.host.hass.states).filter(t=>t.startsWith("lock.")&&this.host._openingEntityAvailable(t)).map(t=>({value:t,label:this.host.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_toggleOpeningEntityPicker(t){const e=this.host._openingDialog;if(!e)return;const s="contact"===t?!e.contactOpen:!e.lockOpen;this.host._openingDialog={...e,contactOpen:"contact"===t&&s,lockOpen:"lock"===t&&s}}_filterOpeningEntities(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contactFilter:e}:{...s,lockFilter:e})}_selectOpeningEntity(t,e){const s=this.host._openingDialog;s&&(this.host._openingDialog="contact"===t?{...s,contact:e,contactOpen:!1,contactFilter:""}:{...s,lock:e,lockOpen:!1,lockFilter:""})}_mergeClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms,i=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const o=i.id;if(!this.host._mergeSel||this.host._mergeSel===o)return void(this.host._mergeSel=this.host._mergeSel===o?null:o);const a=s.find(t=>t.id===this.host._mergeSel),n=a?V(a):null,r=V(i),l=n&&r?ms(n,r):null;if(!l)return this.host._showToast(this.host._t("toast.merge_not_adjacent")),void(this.host._mergeSel=null);this.host._mergeDialog={aId:this.host._mergeSel,bId:o,poly:l,pick:"a"},this.host._mergeSel=null}_commitMerge(){const t=this.host._mergeDialog,e=this.host._curSpaceCfg;if(!t||!e||!this.host._spaceModel())return;const s=this._geometrySnapshot(),i=this.host._spaceH,o="a"===t.pick?t.aId:t.bId,a="a"===t.pick?t.bId:t.aId,n=e.rooms.find(t=>t.id===o);if(!n)return void(this.host._mergeDialog=null);n.poly=t.poly.map(t=>[t[0]/lh,t[1]/i]),delete n.x,delete n.y,delete n.w,delete n.h,e.rooms=e.rooms.filter(t=>t.id!==a);const r=this._commitPhysicalGeometry(this.host._t("history.merge_rooms"),s);this.host._mergeDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),r&&this.host._showToast(this.host._t("toast.rooms_merged",{name:n.name||""}))}_splitClick(t){const e=this.host._spaceModel();if(!e)return;const s=e.rooms;if(!this.host._splitSel){const e=[...s].reverse().find(e=>this._pointInRoom(t,e));if(!e?.id)return;return void(this.host._splitSel={roomId:e.id,pts:[]})}const i=s.find(t=>t.id===this.host._splitSel.roomId),o=i?V(i):null;if(!i||!o)return void(this.host._splitSel=null);const a=.02*this.host._gridPitch,n=6*this.host._gridPitch,r=gs(t,o),l=r&&fs(r,o,this.host._gridPitch)||r,h=r&&l&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?l:null,c=!!h&&bs(h,o,a),d=this.host._splitSel.pts;if(!d.length)return c?void(this.host._splitSel={...this.host._splitSel,pts:[h]}):void this.host._showToast(this.host._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return ge(e,o,a)?void(this.host._splitSel={...this.host._splitSel,pts:[...d,e]}):void this.host._showToast(this.host._t("toast.split_pick_inside"))}const p=be(o,[...d,h],a);if(!p)return void this.host._showToast(this.host._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=R(u)>=R(_)?u:_,g=m===u?_:u;this.host._pendingSplit={roomId:i.id,mainPoly:m,newPoly:g},this.host._cursorPt=null,this.host._nameSel="",this.host._areaSel="",this.host._roomDialog=!0}_markupMove(t){if(!this.host._markup)return;if("column"===this.host._tool)return void(this.host._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this.host._tool||"wallthick"===this.host._tool)return void(this.host._cursorPt=this._svgPoint(t));const e="draw"===this.host._tool&&!this.host._contourClosed,s="split"===this.host._tool&&!!this.host._splitSel?.pts?.length;if(!e&&!s)return;const i=this._svgPoint(t);if(e){const e=this._resolvePlanDrawPoint(i,t.shiftKey);return this.host._planSnapHover={contextKey:e.contextKey,candidate:e.candidate,conflicts:e.conflicts},this._syncPlanSnapConflictMarkers(e.conflicts),this.host._path.length?void(this.host._cursorPt=e.point):void this._syncPlanSnapActiveMarker(e.candidate)}this.host._cursorPt=this._snap(i)}_saveRoom(){(this.host._areaSel||this.host._nameSel.trim())&&(this.host._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(t){const e=this.host._wallFaceBatch;if(!e)return;const s=e.candidates[e.index];if(!s)return;if(!t&&(s.existing||s.repair)&&1===e.candidates.length)return this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate();const i=t?{candidate:s,create:!0,name:this.host._nameSel.trim(),area:this.host._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:s,create:!1},o=[...e.decisions,i],a=e.index+1;if(a"number"==typeof t&&Number.isFinite(t)&&t>=0?t:null,o=.02*this.host._gridPitch,a=this.host._spaceModel();if(!a)return X;for(const e of this._planSnapGeometrySnapshot().value.segments){if("room"===e.sourceKind||G(t,[e.a[0],e.a[1],e.b[0],e.b[1]])>o)continue;if("partition"===e.sourceKind)return i(a.partitions.find(t=>t.id===e.sourceId)?.cm)??X;const s=e.sourceId.lastIndexOf(":"),n=s>=0?e.sourceId.slice(0,s):e.sourceId,r=s>=0?Number(e.sourceId.slice(s+1)):-1;return i(a.room_drafts.find(t=>t.id===n)?.segments[r]?.cm)??X}const n=xn(Math.max(0,e.length-1),s,this.host._drawWallCm,X);for(let s=0;s+1[...t]);if(!e?.sourceKey.startsWith("active:"))return s;const i=/:(\d+)$/.exec(e.sourceKey),o=i?Number(i[1])+("b"===e.endpoint?1:0):-1;return o>=0&&oe.key===t.sourceKey),a=i.find(e=>e.key===t.targetSourceKey);if(!o||!a)return!1;const n="a"===t.endpoint?o.a:o.b;return!(Math.hypot(n[0]-t.from[0],n[1]-t.from[1])>s)&&(!(G(t.to,[a.a[0],a.a[1],a.b[0],a.b[1]])>s)&&!function(t,e){if(!t.sourceKey.startsWith("static:partition|"))return!1;const s=t.sourceKey.slice(17).split("|")[0];return!!s&&e.some(t=>"partition"===t.host?.kind&&t.host.id===s)}(t,this.host._curSpaceCfg?.openings||[]))}_applyWallRepair(t,e){const s=this.host._curSpaceCfg;if(!s||!this._validateWallRepair(t,e.activePath))return!1;const i=e=>{e[0]=t.to[0]/lh,e[1]=t.to[1]/lh};if(t.sourceKey.startsWith("active:")){const o=/:(\d+)$/.exec(t.sourceKey),a=o?Number(o[1])+("b"===t.endpoint?1:0):-1;if(a<0||a>=e.activePath.length)return!1;e.activePath[a]=[...t.to];const n=(s.room_drafts||[]).find(t=>t.id===e.activeDraftId);return n?.points?.[a]&&i(n.points[a]),!0}if(!t.sourceKey.startsWith("static:"))return!1;const[o,a]=t.sourceKey.slice(7).split("|");if("partition"===o){const e=(s.partitions||[]).find(t=>t.id===a);if(!e)return!1;const o=[e.a[0]*lh,e.a[1]*lh],n=[e.b[0]*lh,e.b[1]*lh],r=Math.hypot(o[0]-t.from[0],o[1]-t.from[1])<=2e-4*this.host._gridPitch?e.a:Math.hypot(n[0]-t.from[0],n[1]-t.from[1])<=2e-4*this.host._gridPitch?e.b:null;return!!r&&(i(r),!0)}if("draft"===o){const e=a.lastIndexOf(":"),o=e>=0?a.slice(0,e):"",n=e>=0?Number(a.slice(e+1)):-1,r=(s.room_drafts||[]).find(t=>t.id===o);if(!r?.points?.[n]||!r?.points?.[n+1])return!1;const l=[n,n+1].find(e=>Math.hypot(r.points[e][0]*lh-t.from[0],r.points[e][1]*lh-t.from[1])<=2e-4*this.host._gridPitch);return null!=l&&(i(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const t=this.host._wallFaceBatch,e=this.host._curSpaceCfg,s=this.host._spaceModel();if(!t||!e||!s)return;const i=(t,e)=>{this.host._showToast(this.host._t(t,e)),this._roomDialogCancel()},o=t.decisions.filter(t=>t.create),a=o.map(t=>t.candidate.ring),n=s.rooms;for(let t=0;ts.id!==e&&!!V(s)&&I(a[t],V(s)));if(s)return void i("toast.room_overlap",{name:s.name||""})}if((e.rooms||[]).length+o.length>400)return void i("toast.physical_limit");if(o.some(t=>t.candidate.split&&!e.rooms.some(e=>e.id===t.candidate.split.roomId)))return void i("toast.contour_cannot_close");const r=o.map(t=>t.candidate.repair).filter(t=>!!t);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],t.activePath))return void i("toast.wall_repair_changed");const l=this._activePathWithRepair(t.activePath,r[0]),h=2e-4*this.host._gridPitch,c=new Map;for(let e=0;et.candidate.atomKeys)),e=o.some(t=>t.candidate.consumeAllActive);for(const s of Un(this._wallGraphSources(l),h)){const i=s.sourceKeys.find(t=>c.has(t));!i||e||t.has(s.key)||d.push({a:s.a,b:s.b,cm:c.get(i)})}}else d.push(...Sn(l,xn(l.length-1,t.activeCms,this.host._drawWallCm,X)));if((e.partitions||[]).length+d.length>hh)return void i("toast.physical_limit");const p=o.map(t=>t.candidate.ring.map(()=>"")),u=t.activeDraftId?(e.room_drafts||[]).find(e=>e.id===t.activeDraftId):null,_=this._draftSegmentsForPath(t.activePath,u,t.activeCms),m=(t,e)=>{const s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`,i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`;return s<=i?`${s}|${i}`:`${i}|${s}`};for(let e=0;e<_.length;e++){const s=_[e]?.id;if(!s)continue;const i=t.activePath[e],a=t.activePath[e+1],n=[i[0],i[1],a[0],a[1]],r=[];o.forEach((t,e)=>t.candidate.ring.forEach((s,i)=>{const o=t.candidate.ring[(i+1)%t.candidate.ring.length];G(s,n)<=h&&G(o,n)<=h&&r.push({kind:"room",owner:e,edge:i,a:s,b:o})})),d.forEach((t,e)=>{G(t.a,n)<=h&&G(t.b,n)<=h&&r.push({kind:"partition",owner:e,edge:0,a:t.a,b:t.b})});const l=[(i[0]+a[0])/2,(i[1]+a[1])/2];r.sort((t,e)=>{const s=[t.a[0],t.a[1],t.b[0],t.b[1]],o=[e.a[0],e.a[1],e.b[0],e.b[1]];return(G(l,s)<=h?0:1)-(G(l,o)<=h?0:1)||(G(i,s)<=h?0:1)-(G(i,o)<=h?0:1)||m(t.a,t.b).localeCompare(m(e.a,e.b))});const c=r[0];if(!c)continue;const u=m(c.a,c.b);for(const t of r)m(t.a,t.b)===u&&("room"===t.kind?p[t.owner][t.edge]=s:d[t.owner].id=s)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],t))return void i("toast.wall_repair_changed");const f=o.some(t=>!!t.candidate.split),b=f?Ut(s.rooms,e.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh):null,v=f&&Array.isArray(e.walls)?e.walls:null;for(const t of o){const s=t.candidate.split;if(!s)continue;const o=e.rooms.find(t=>t.id===s.roomId);if(!o)return void i("toast.contour_cannot_close");o.poly=s.mainPoly.map(t=>[t[0]/lh,t[1]/this.host._spaceH]),delete o.x,delete o.y,delete o.w,delete o.h}const y=Date.now().toString(36),$=[];if(o.forEach((t,s)=>{const i=t.area?this.host.hass.areas[t.area]?.name:"",o={id:`r${y}-${s}`,name:t.name||i||this.host._t("room.default_name"),area:t.area||null,poly:t.candidate.ring.map(t=>[t[0]/lh,t[1]/this.host._spaceH]),...p[s].some(Boolean)?{wall_ids:p[s]}:{},...t.settings?{settings:JSON.parse(JSON.stringify(t.settings))}:{}};e.rooms.push(o),$.push({room:o,decision:t})}),t.activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==t.activeDraftId),e.room_drafts.length||delete e.room_drafts),d.length&&(e.partitions||=[],d.forEach((t,s)=>e.partitions.push({id:t.id||`partition-${y}-${s}`,a:[t.a[0]/lh,t.a[1]/lh],b:[t.b[0]/lh,t.b[1]/lh],cm:t.cm}))),f){const t=this.host._normalizeWalls(b,this.host._openCuts());t.length?e.walls=t:v?.length?e.walls=v:delete e.walls}if($.length){this.host._cfgEpoch++;const s=this.host._openCuts(),i=this.host._spaceModel();if(!i)return;let o=e.walls;for(const{room:e,decision:a}of $){const n=a.candidate.ring,r=this._wallSourceCmAt(n[0],t.activePath,t.activeCms);o=vs(o,i.rooms,e.id,r,this.host._wallKeyPitch,s,lh);for(const a of Z(i.rooms,o,s,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh)){if(a.roomId!==e.id||"outer"!==a.kind)continue;const s=[(a.a[0]+a.b[0])/2,(a.a[1]+a.b[1])/2];o=et(o,a.a,a.b,this._wallSourceCmAt(s,t.activePath,t.activeCms),this.host._wallKeyPitch,lh)}}const a=this.host._normalizeWalls(o,s);a.length?e.walls=a:delete e.walls}if(!this._commitPhysicalGeometry(this.host._t("history.wall_face_batch"),g))return;delete this.host._resumeDraftBySpace[this.host._space],this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._wallFaceBatch=null,this.host._roomDialog=!1,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices();const w=new Set(o.map(t=>t.area).filter(t=>!!t));if(w.size){const t={...this.host._layout};for(const e of this.host._devices){if(!w.has(e.area||"")||e.space!==this.host._space)continue;if(this.host._layout[e.id])continue;const s=this.host._defPos[e.id];s&&(t[e.id]={s:this.host._space,x:s.x/lh,y:s.y/lh},this.host._dirtyPos.add(e.id))}this.host._layout=t,this.host._persistLayout()}this.host._showToast(this.host._t(o.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:o.length}))}_commitRoom(){const t=this.host._curSpaceCfg,e=this.host._spaceModel();if(!t||!e)return;const s=this._geometrySnapshot(),i=this.host._spaceH,o=!!this.host._pendingSplit,a=o?Ut(e.rooms,t.walls,this.host._openCuts(),this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh):null,n=o&&Array.isArray(t.walls)?t.walls:null;let r;if(this.host._pendingSplit){const e=t.rooms.find(t=>t.id===this.host._pendingSplit.roomId);if(!e)return this.host._pendingSplit=null,this.host._splitSel=null,void(this.host._roomDialog=!1);e.poly=this.host._pendingSplit.mainPoly.map(t=>[t[0]/lh,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this.host._pendingSplit.newPoly}else{if(!this.host._contourClosed)return;r=this.host._path.slice(0,-1)}const l=this.host._areaSel?this.host.hass.areas[this.host._areaSel]?.name:"",h={id:"r"+Date.now().toString(36),name:this.host._nameSel||l||this.host._t("room.default_name"),area:this.host._areaSel||null,poly:r.map(t=>[t[0]/lh,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!o&&this.host._activeDraftId){const e=Array.isArray(t.room_drafts)?t.room_drafts.find(t=>t.id===this.host._activeDraftId):null,s=xn(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,X),i=this._draftSegmentsForPath(this.host._path,e,s);i.length===r.length&&i.every(t=>!!t.id)&&(h.wall_ids=i.map(t=>t.id))}if(t.rooms.push(h),!o&&this.host._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this.host._activeDraftId),t.room_drafts.length||delete t.room_drafts),o){const e=this.host._normalizeWalls(a,this.host._openCuts());e.length?t.walls=e:n?.length?t.walls=n:delete t.walls}if(!o){const e=xn(r.length,[...this.host._draftSegmentCms,this.host._closingWallCm??void 0],this.host._drawWallCm,X),s=e[0];if(null!=s){this.host._cfgEpoch++;const i=this.host._openCuts(),o=this.host._spaceModel();if(!o)return;let a=vs(t.walls,o.rooms,h.id,s,this.host._wallKeyPitch,i,lh);for(const t of Z(o.rooms,a,i,this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh)){if(t.roomId!==h.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],i=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return G(s,[t[0],t[1],i[0],i[1]])<=.02*this.host._gridPitch});i>=0&&(a=et(a,t.a,t.b,e[i],this.host._wallKeyPitch,lh))}a=this.host._normalizeWalls(a,i),a.length?t.walls=a:delete t.walls}}const c=this._commitPhysicalGeometry(this.host._t(o?"history.split_room":"history.add_room"),s);this.host._path=[],delete this.host._resumeDraftBySpace[this.host._space],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this.host._pendingSplit=null,this.host._splitSel=null;const d=this.host._areaSel;if(this.host._areaSel="",this.host._nameSel="",this.host._roomDialog=!1,this.host._regSignature="",this.host._maybeRebuildDevices(),!c)return;let p=0;if(d){const t=lh,e={...this.host._layout};for(const s of this.host._devices){if(s.area!==d||s.space!==this.host._space)continue;if(p++,this.host._layout[s.id])continue;const i=this.host._defPos[s.id];i&&(e[s.id]={s:this.host._space,x:i.x/lh,y:i.y/t},this.host._dirtyPos.add(s.id))}this.host._layout=e,this.host._persistLayout()}const u=this.host._model.find(t=>t.id===this.host._space)?.rooms.length||0;this.host._showToast(d?this.host._t("toast.room_saved",{n:u,added:p}):this.host._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this.host._wallRepairDiagnostic=null,this.host._activeDraftId&&(this.host._resumeDraftBySpace[this.host._space]=this.host._activeDraftId),this.host._path=[],this.host._activeDraftId=null,this.host._draftSegmentCms=[],this.host._closingWallCm=null,this._clearPlanSnapHover(),this.host._roomDialog=!1,this.host._pendingSplit=null,this.host._wallFaceBatch=null,this.host._splitSel=null,this.host._mergeSel=null,this.host._mergeDialog=null,this.host._physicalSel=null,this.host._physicalDrag=null,this.host._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this.host._resumeDraftBySpace[this.host._space];if(!t)return;const e=this.host._spaceModel()?.room_drafts.find(e=>e.id===t);e?(this.host._activeDraftId=t,this.host._path=e.points.map(t=>[...t]),this.host._draftSegmentCms=this._adoptDraftCms(this.host._path,e.segments.map(t=>t.cm),e.id),this._clearPlanSnapHover()):delete this.host._resumeDraftBySpace[this.host._space]}_roomDialogCancel(){if(this.host._roomDialog=!1,this.host._roomEditId)return this.host._roomEditId=null,this.host._nameSel="",void(this.host._areaSel="");if(this.host._wallFaceBatch){const t=this.host._wallFaceBatch;return this.host._wallFaceBatch=null,this.host._path=t.activePath.map(t=>[...t]),this.host._draftSegmentCms=[...t.activeCms],this.host._activeDraftId=t.activeDraftId,this.host._nameSel="",this.host._areaSel="",void this.host.requestUpdate()}if(this.host._pendingSplit)return this.host._pendingSplit=null,void(this.host._splitSel=null);this.host._undoPoint()}_openDeviceInbox(){this.host._deviceInboxReturn=null,this.host._deviceInbox=this.host._deviceInbox||{tab:"on_plan",search:"",showEntities:!1,onlyNew:!1,limit:100}}_closeMarkerDialog(){if(this.host._markerDialog=null,this.host._deviceInboxReturn){const t={...this.host._deviceInboxReturn};this.host._deviceInbox=t,this.host._deviceInboxReturn=null,t.anchor&&this.host.updateComplete.then(()=>requestAnimationFrame(()=>{const e=`.device-inbox-row[data-binding="${CSS.escape(t.anchor)}"]`;this.host.renderRoot.querySelector(e)?.scrollIntoView({block:"nearest"})}))}}_deviceInboxCandidates(t){return en({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:t,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}})}_deviceInboxRows(){const t=this.host._deviceInbox||this.host._deviceInboxReturn,e=!!t?.showEntities,s=[this.host._haRegistry.revision,this.host._cfgRev,this.host._cfgEpoch,this.host._regSignature,this.host._newSyncKey,e?1:0,this.host._showAll?1:0,l(this.host.hass,this.host._config?.language)].join("|");if(this.host._deviceInboxMemo?.key===s)return this.host._deviceInboxMemo.rows;const i=this._deviceInboxCandidates(e),o=new Set(i.map(t=>t.value));for(const t of this.host._markers)t.binding&&"virtual"!==t.binding&&o.add(t.binding);for(const t of this.host._devices)t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&o.add(`${t.bindingKind}:${t.bindingRef}`);const a=new Map;for(const t of o)a.set(t,this.host._bindingStatus(t));const n=this.host._areaToSpace,r={};for(const[t,e]of Object.entries(this.host.hass?.areas||{}))r[t]=n[t]?.room?.name||e?.name||t;for(const[t,e]of Object.entries(n))r[t]=e.room.name||r[t]||t;const h=Object.fromEntries(this.host._model.map(t=>[t.id,t.title])),c=Object.fromEntries(Object.entries(n).map(([t,e])=>[t,e.space])),d={},p=new Map;for(const[t,e]of Object.entries(this.host._fullRegistryHass.entities||{})){const s=String(e?.platform||"").trim();if(s&&(d[`entity:${t}`]=s),s&&e?.device_id){const t=p.get(e.device_id)||new Set;t.add(s),p.set(e.device_id,t)}}for(const[t,e]of p)d[`device:${t}`]=[...e].sort().join(", ");const u={};for(const[t,e]of Object.entries(this.host._fullRegistryHass.devices||{})){const s=`device:${t}`,i=p.get(t)||new Set,o=Array.isArray(e?.identifiers?.[0])?String(e.identifiers[0][0]||""):"";o&&!d[s]&&(d[s]=o);const a=[o,...i].some(t=>this.host._excluded.has(t));"service"===e?.entry_type?u[s]="service_entry":a?u[s]="excluded_integration":"Group"===e?.model?u[s]="grouped_light":/scene/i.test(e?.model||"")?u[s]="excluded_domain":(/bridge/i.test(`${e?.model||""}${e?.name||""}`)||"myheat"===o&&e?.via_device_id)&&(u[s]="represented_by_parent")}const _=function(t){const{devices:e,markers:s,candidates:i,statuses:o,newDeviceIds:a,showHiddenOnPlan:n,areaNames:r={},spaceNames:l={},spaceByArea:h={},integrationByBinding:c={},reasonByBinding:d={}}=t,p=new Map;for(const t of e)!t.virtual&&t.bindingKind&&"virtual"!==t.bindingKind&&t.bindingRef&&p.set(`${t.bindingKind}:${t.bindingRef}`,t);const u=new Map,_=new Map;for(const t of s)t?.binding&&"virtual"!==t.binding&&(t.removed?_.set(t.binding,t):u.set(t.binding,t));const m=new Map(i.map(t=>[t.value,t])),g=new Set([...p.keys(),...u.keys(),...m.keys(),...[..._.keys()].filter(t=>m.has(t))]),f=[];for(const t of g){const e=p.get(t),s=u.get(t),i=s?void 0:_.get(t),g=m.get(t);let b;if(i&&g)b="readd";else if(!0===s?.hidden)b="hidden";else if(e||s)b="on_plan";else{if(!g)continue;b="available"}const v=e?.bindingStatus||o.get(t)||sn,y=s||i,$=on(t),w=e?.area||g?.areaId||y?.area||"",k=e?.space||y?.space||h[w]||"",x=e?.name||y?.name||g?.label||t,S=e?.model||y?.model||g?.model||"",M=!!g?.parentDeviceId&&p.has(`device:${g.parentDeviceId}`),D="readd"===b?"removed":"hidden"===b?s&&an(s)?d[t]||"automatic_hidden":"manual_hidden":"on_plan"===b?s?"visible_explicit":"visible_auto":d[t]||(M?"represented_by_parent":w&&k?"available":"no_bound_room"),C=!!e&&(!e.hidden||n),P="active"===v.kind,I=C&&(P||"ha_disabled"===v.kind&&n),T=[x,S,c[t],r[w],l[k],t,g?.sub].filter(Boolean).join(" ").toLocaleLowerCase();f.push({key:t,binding:t,category:b,status:v,reason:D,deviceId:e?.id,markerId:y?.id,name:x,icon:e?.icon||y?.icon||("entity"===$?"mdi:code-braces":"mdi:devices"),model:S,integration:c[t]||"",areaId:w,areaName:r[w]||"",spaceId:k,spaceName:l[k]||"",kind:$,isNew:!!e&&a.has(e.id),searchText:T,canFind:I,canEdit:!!e||!!s,canHide:"on_plan"===b&&P,canShow:"hidden"===b&&P,canAdd:("available"===b||"readd"===b)&&P})}const b=t=>"active"===t.status.kind?1:0;return f.sort((t,e)=>t.category.localeCompare(e.category)||Number(e.isNew)-Number(t.isNew)||b(t)-b(e)||t.name.localeCompare(e.name)||t.binding.localeCompare(e.binding))}({devices:this.host._devices,markers:this.host._markers,candidates:i,statuses:a,newDeviceIds:this.host._newIds,showHiddenOnPlan:this.host._showAll,areaNames:r,spaceNames:h,spaceByArea:c,integrationByBinding:d,reasonByBinding:u});return this.host._deviceInboxMemo={key:s,rows:_},_}_deviceForInboxRow(t){const e=t.deviceId?this.host._devices.find(e=>e.id===t.deviceId):null;if(e)return e;const s=t.markerId?this.host._markers.find(e=>e.id===t.markerId):null;return!s||s.removed?null:{id:s.id,name:t.name,model:t.model,area:t.areaId,space:t.spaceId||this.host._space,hidden:!0===s.hidden||"ha_disabled"===t.status.kind,userHidden:!0===s.hidden,bindingStatus:t.status,icon:t.icon,entities:"active"===t.status.kind?t.status.enabledEntityIds:[],allEntities:t.status.allEntityIds,primary:"active"===t.status.kind?t.status.enabledEntityIds[0]:void 0,marker:s,bindingKind:t.kind,bindingRef:t.binding.slice(t.binding.indexOf(":")+1),pdfs:s.pdfs||[]}}_openInboxMarker(t,e=!1){const s=this.host._deviceInbox;if(s){if(this.host._deviceInboxReturn={...s,anchor:t.key},this.host._deviceInbox=null,!e){const e=this._deviceForInboxRow(t);return void(e?this._openMarkerDialog(e):this._closeMarkerDialog())}this._openMarkerDialog(),this.host._markerDialog?this.host._markerDialog={...this.host._markerDialog,bindingMode:"ha",binding:t.binding,bindingOpen:!1,showEntities:"entity"===t.kind,name:""}:this._closeMarkerDialog()}}async _setInboxHidden(t,e){const s=this.host._deviceInbox,i=this.host._serverCfg;if(!s||!i||s.busy||"active"!==t.status.kind)return;const o=i.markers||[],a=o.find(e=>!e.removed&&e.binding===t.binding);if(!e&&!a)return;const n=a?.id||ys(t.binding,t.markerId,()=>`m_${Date.now().toString(36)}`),r=a?{...a,hidden:e}:{id:n,binding:t.binding,hidden:!0};i.markers=[...o.filter(e=>e.id!==n&&(e.binding!==t.binding||!0===e.removed)),r],this.host._deviceInbox={...s,busy:t.key,anchor:t.key};try{await this._saveConfigNow(),this.host._regSignature="",this.host._deviceInboxMemo=null,this.host._maybeRebuildDevices(),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("device_inbox.saved"))}catch(t){this.host._serverCfg===i&&(i.markers=o),this.host._deviceInbox&&(this.host._deviceInbox={...this.host._deviceInbox,busy:void 0}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_findInboxDevice(t){if(!t.canFind)return;const e=this._deviceForInboxRow(t);if(!e)return;if(this.host._deviceInbox=null,e.space&&e.space!==this.host._space){if(!this.host._commitSpace(e.space))return;this.host._restoreZoom()}const s=()=>{const t=this.host._devices.find(t=>t.id===e.id)||e,s=this.host._pos(t);this.host._applyView(this.host._zoom,s.x,s.y),this.host._selId=t.id,this.host.requestUpdate(),window.setTimeout(()=>{this.host._selId===t.id&&(this.host._selId=null,this.host.requestUpdate())},1500)};requestAnimationFrame(()=>requestAnimationFrame(s))}_openMarkerDialog(t){if(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null,t&&this.host._ackNewDevice(t.id),this.host._norm)if(t){const e=t.marker,s=Object.prototype.hasOwnProperty.call(e||{},"is_light"),i=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),o=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),a=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),n=Object.prototype.hasOwnProperty.call(e||{},"value_source"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),l=Object.prototype.hasOwnProperty.call(e||{},"toggle_entity"),h=$s(e?.glow_color),c=this.host._devicePresentation(t,!0).valueBadge,d=ws(this.host._planHass,t,this.host._devices),p=ks(this.host._planHass,t,d);this.host._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this.host._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ms(t.marker?.display),rippleColor:b(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):1.5,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Ss(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:e?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:l,originalToggleEntity:e?.toggle_entity,tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:xs(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:s,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:i,originalLightEntity:e?.light_entity,glowMode:null!=h?.bri?"fixed":h?"color":"auto",glowColor:h?.c||this.host._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(h?.bri??1))),glowColorDrafted:!!h,glowBrightnessDrafted:null!=h?.bri,glowTouched:!1,originalHasGlowColor:o&&(!!h||null===e?.glow_color),originalGlowColor:h||(null===e?.glow_color?null:void 0),valueBadgeEnabled:a?!0===e?.value_badge?.enabled:!!c,valueBadgeSource:e?.value_badge?.source||c?.source||p,valueBadgePosition:e?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:a,originalValueBadge:e?.value_badge,valueSource:e?.value_source||null,valueSourceTouched:!1,originalHasValueSource:n,originalValueSource:e?.value_source,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this.host._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",roomTouched:!1,hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this.host._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:1.5,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",toggleEntity:"",toggleEntityTouched:!1,originalHasToggleEntity:!1,originalToggleEntity:void 0,tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this.host._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,valueSource:null,valueSourceTouched:!1,originalHasValueSource:!1,originalValueSource:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",roomTouched:!1,hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this.host._showToast(this.host._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ds)for(const[s,i]of Object.entries(this.host.hass.states))s.startsWith(e+".")&&this.host._planEntityAvailable(s)&&t.push({value:s,label:i?.attributes?.friendly_name||s,sub:this.host._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=en({hass:this.host._planHass,devices:this.host._devices,markers:this.host._markers,showEntities:!!this.host._markerDialog?.showEntities,currentBinding:this.host._markerDialog?.binding,currentDeviceId:this.host._markerDialog?.devId,labels:{device:this.host._t("marker.sub_device"),z2mGroup:this.host._t("marker.sub_z2m_group"),group:this.host._t("marker.sub_group"),helper:this.host._t("marker.sub_helper"),entity:this.host._t("marker.sub_entity")}}),e=(this.host._markerDialog?.bindingFilter||"").toLowerCase().trim(),s=e?t.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(e)):t;return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,s=this.host._t(t);if(s!==t)return s}return this.host._errText(t)}async _pickMarkerFiles(t){const e=t.target,s=e.files?[...e.files]:[];if(e.value="",!s.length||!this.host._markerDialog)return;const i=this.host._markerDialog.uploadId||this.host._markerDialog.devId||"new",o=[];for(const t of s)try{const e=new FormData;e.append("marker_id",i),e.append("file",t,t.name);const s=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}),a=await s.json().catch(()=>({}));if(!s.ok||a.error){const t={too_large:this.host._t("err.too_large",{mb:a.max_mb||50}),bad_ext:this.host._t("err.bad_ext"),unauthorized:this.host._t("err.unauthorized")};throw new Error(t[a.error]||a.error||"HTTP "+s.status)}o.push({name:a.name||t.name,url:a.url})}catch(e){this.host._showToast(this.host._t("toast.file_failed",{name:t.name,err:this.host._errText(e)}))}o.length&&this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:[...this.host._markerDialog.pdfs,...o]},this.host._showToast(this.host._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,pdfs:this.host._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const s={c:b(t.glowColor,this.host._fillColors.glow_light.c)};"fixed"===t.glowMode&&(s.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=s}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}_markerToggleEntityFields(t){return(e={touched:t.toggleEntityTouched,originalHas:t.originalHasToggleEntity,original:t.originalToggleEntity,value:t.toggleEntity}).touched?e.value?{toggle_entity:e.value}:{}:e.originalHas?{toggle_entity:e.original??null}:{};var e}async _saveMarker(){const t=this.host._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return;if("virtual"===t.binding&&!t.name.trim())return void this.host._showToast(this.host._t("toast.virtual_name_required"));if("run"===e&&!t.tapTarget)return void this.host._showToast(this.host._t("toast.run_target_required"));if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)return void this.host._showToast(this.host._t("toast.value_badge_source_required"));if("ha"===t.bindingMode){const e=this.host._bindingStatus(t.binding),s=t.devId?this.host._markers.find(e=>e.id===t.devId):null,i=!s||s.binding!==t.binding;if("active"!==e.kind&&i)return void this.host._showToast(this.host._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===s?.hidden&&!t.hideFromPlan)return this.host._markerDialog={...t,hideFromPlan:!0},void this.host._showToast(this.host._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const s=this.host._serverCfg;if(!s)return;const i=this.host._cfgRev,o=_(s);let a=JSON.parse(JSON.stringify(s));const n=a.markers||[],r=ys(t.binding,t.devId,()=>"v_"+Date.now().toString(36)),l=t.devId,h=l?this.host._devices.find(t=>t.id===l):null,c=l?n.find(t=>t.id===l):null,d=!!c&&("string"==typeof c.area&&c.area.length>0||null===c.area&&!!c.space&&!!c.room_id),p="virtual"===t.binding||t.roomTouched||d,g="virtual"===t.binding||t.roomTouched?Cs(t.room):null;let f=d&&!t.roomTouched?c?.space||null:g?.space||null;const b=d&&!t.roomTouched?c?.area||null:g?.area||null,v=d&&!t.roomTouched?c?.room_id||null:g?.roomId||null,y=f||h?.space||null,$=y?this.host._spaceModelById(y):this.host._spaceModel();if(!$)return;const w=$.id;"virtual"!==t.binding||f||(f=w),this.host._markerDialog={...t,busy:!0};let k=null,x=!1;try{const c="virtual"===t.binding?[]:n.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),d=c.length>0,m=xs(t.binding,t.controls,this.host._bindingEntities(t.binding)),g=n.find(t=>t.id===r||t.id===l)?.vacuum||null,y={id:r,vacuum:g,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:m.length?m:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=nh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};p&&(y.space=f,y.area=b,y.room_id=v);const S=h?.marker?.room_id??null,M=t.roomTouched&&null!=h&&(h.space!==f||h.area!==b||S!==v);let D=!1;const C=t.uploadId||l;if(C&&C!==r&&y.pdfs?.length)try{const t=await this.host.hass.callWS({type:"houseplan/files/migrate",from_id:C,to_id:r}),e=t?.mapping||{};y.pdfs=Ps(y.pdfs,C,r,e),D=Object.keys(e).length>0}catch(t){this.host._showToast(this.host._t("toast.files_migrate_failed",{err:this.host._errText(t)}))}a.markers=n,l&&l!==r&&(a.markers=Is(a.markers,l,r)),l&&l!==r&&"derived_marker_state"===y.value_badge?.source?.kind&&y.value_badge.source.ref===`marker:${l}`&&(y.value_badge.source={kind:"derived_marker_state",ref:`marker:${r}`}),l&&l!==r&&"derived_marker_state"===y.value_source?.kind&&y.value_source.ref===`marker:${l}`&&(y.value_source={kind:"derived_marker_state",ref:`marker:${r}`}),a.markers=a.markers.filter(t=>t.id!==r&&t.id!==l&&("virtual"===y.binding||t.binding!==y.binding)),a.markers.push(y);const P=new Set(c);l&&l!==r&&P.add(l),P.delete(r),P.size&&a.settings?.marker_area_snapshot&&(a.settings={...a.settings,marker_area_snapshot:Ts(a.settings.marker_area_snapshot,P)});let I=null;const T=l?this.host._layout[l]:null,R=T?{s:T.s||h?.space||this.host._space,x:T.x,y:T.y}:l&&h&&this.host._defPos[l]?this.host._normPos(h.space,this.host._defPos[l].x,this.host._defPos[l].y):null;if(!d&&R&&R.s===w)r===l&&this.host._layout[r]&&!M||(I={s:R.s,x:R.x,y:R.y});else if(d||!this.host._layout[r]||M){let t=$.vb[0]+$.vb[2]/2,e=$.vb[1]+$.vb[3]/2;const s=v?$.rooms.find(t=>t.id===v):b?$.rooms.find(t=>t.area===b):void 0;s&&([t,e]=this.host._roomCenter(s)),I=this.host._normPos(w,t,e)}if(this.host._serverCfg!==s||this.host._cfgRev!==i||_(s)!==o)return this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),void this.host._showToast(this.host._t("toast.conflict"));if(a=this._prepareConfigCandidate(a),k=u(s,a,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=a,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.cancel(),await this._saveConfigNow(k),x=!0,I){const t=Rs(I);this.host._layout={...this.host._layout,[r]:t},this.host._noteLayoutRev(await this.host.hass.callWS({type:"houseplan/layout/update",device_id:r,pos:t}))}const z=new Set(c);l&&l!==r&&z.add(l),z.delete(r);for(const t of z)delete this.host._layout[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{});D&&C&&await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:C}).catch(()=>{}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_saved"))}catch(t){!x&&k&&(m(this.host,k,_),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate()),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteMarker(){const t=this.host._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this.host._devices.find(e=>e.id===t.devId):null,s=this.host._markers.find(e=>e.id===t.devId);if(!e&&!s)return;const i=e?"virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"":s.binding;if(!i)return;const o=t.name||this.host._t("device.fallback"),a=t.devId,n=await this.host._confirmDanger({key:"remove-marker",kind:"destructive",title:this.host._t("confirm.remove_marker_title"),message:this.host._t("confirm.remove_marker_body"),objectName:o,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._markerDialog;if(!n||!r||r.busy||r.devId!==a)return;const l=this.host._devices.find(t=>t.id===a),h=this.host._markers.find(t=>t.id===a);if(!l&&!h)return;const c=this.host._serverCfg;if(!c)return;c.markers=c.markers||[];const d=c.markers,p=l?"virtual"===l.bindingKind?"virtual":l.bindingKind&&l.bindingRef?`${l.bindingKind}:${l.bindingRef}`:"":h.binding;if(!p||p!==i)return;const u=zs(c.markers,a,p,"virtual"===p);c.markers=As(u.markers,u.cleanupIds);const _=u.cleanupIds;_.size&&c.settings?.marker_area_snapshot&&(c.settings={...c.settings,marker_area_snapshot:Ts(c.settings.marker_area_snapshot,_)}),this.host._markerDialog={...r,busy:!0};try{await this._saveConfigNow();for(const t of _){delete this.host._layout[t],delete this.host._defPos[t],this.host._dirtyPos.delete(t),this.host._sentPos.delete(t);const e=this.host._activityRt.get(t);e&&clearTimeout(e.timer),this.host._activityRt.delete(t),this.host._vacRt.delete(t),delete this.host._vacSrvTrails[t],await this.host.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this.host._noteLayoutRev(t)).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.host.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this.host._deviceInboxReturn&&(this.host._deviceInboxReturn={...this.host._deviceInboxReturn,tab:"readd",anchor:p}),this._closeMarkerDialog(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._infoCard?.id===a&&this.host._closeInfoCard(),this.host._selId===a&&(this.host._selId=null),this.host._drag?.id===a&&(this.host._drag=null),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.marker_removed"))}catch(t){this.host._serverCfg===c&&(c.markers=d),this.host._markerDialog&&(this.host._markerDialog={...this.host._markerDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_openSpaceDialog(t,e){if(this.host._serverStorage&&this.host._serverCfg)if("edit"===t){const s=this.host._serverCfg.spaces.find(t=>t.id===e);if(!s)return;const i=Fs(s),o=s.settings?.custom_fill&&"object"==typeof s.settings.custom_fill?Ns(s.settings.custom_fill):null,a="none"===i.fill?{...o||Hs,a:0}:o;this.host._spaceDialog={mode:t,spaceId:e,title:s.title,planUrl:s.plan_url||null,planFile:null,source:s.plan_url?"file":"draw",showBorders:i.showBorders,showNames:i.showNames,zeroWallStyle:Es(s),displayTouched:!0,hideDecor:i.hideDecor,hideOpenings:i.hideOpenings,roomColor:i.color,roomOpacity:i.opacity,fillMode:"none"===i.fill?"custom":i.fill,customFill:a,glowEnabled:i.glow,bgColor:i.bgColor,bgMode:"static"===s.settings?.bg_mode||"daynight"===s.settings?.bg_mode?s.settings.bg_mode:null,northDeg:Zt({},s.settings),sunRays:"boolean"==typeof s.settings?.sun_rays?s.settings.sun_rays:null,tempMin:i.tempMin,tempMax:i.tempMax,showLqi:i.showLqi??this.host._config?.show_signal??!0,cardFontScale:i.cardFontScale,labelTemp:i.labelTemp,labelHum:i.labelHum,labelLqi:i.labelLqi,labelLight:i.labelLight,cellCm:Number(s.cell_cm)>0?Number(s.cell_cm):5,cellCmInput:Ls(Number(s.cell_cm)>0?Number(s.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1}}else this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...io(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:js,roomOpacity:Ws,fillMode:"custom",customFill:{...Hs,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:Bs,tempMax:Os,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Gs(this.host._imperial),cellCmInput:Ls(Gs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1};else this.host._showToast(this.host._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,s=e.files?.[0];if(!s||!this.host._spaceDialog)return;e.value="",this._decorAssetGuardReplace=null;const i=await so(s);if("reject"===i.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===i.kind)return void(this.host._backdropGuard=i.state);const o=await oo(s,i.ext,s.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:o})}_renderBackdropGuard(){const t=this._decorAssetGuardReplace;return null!==t?ao(this.host,()=>{},()=>{this.host._backdropGuard=null,this._decorAssetGuardReplace=null},this.host.hass,(e,s)=>this._uploadDecorImage(e,s,t),(this.host._backdropGuard?.file.size||0)<=2097152)??d:ao(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??d}_useServerPlan(t){const e=this.host._spaceDialog;e&&(this.host._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this.host._display(t);if(e){const s=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:0),s.onerror=()=>t(0),s.src=e}),i=this.host._spaceDialog;return i&&i.planUrl===t&&Number.isFinite(s)&&s>0?(this.host._spaceDialog={...i,savedAspect:s},s):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const e=this.host._spaceDialog,s=e?.saved?.find(e=>e.name===t);if(!e||!s||s.used_by.length||s.url===e.planUrl)return;const i=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),o=this.host._spaceDialog,a=o?.saved?.find(e=>e.name===t);if(i&&o&&a&&a.url===s.url&&a.modified===s.modified&&!a.used_by.length&&a.url!==o.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this.host._spaceDialog;e?.saved&&(this.host._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return p`
    ${this.host._t("space.loading")}
    `;const e=t.saved||[];if(!e.length)return p`
    ${this.host._t("space.no_saved")}
    `;return p`
    - ${e.map(e=>{return p` -
    - -
    - ${e.name} - ${s=e.size,s>=1048576?(s/1048576).toFixed(1)+" MB":Math.round(s/1024)+" KB"}${e.used_by.length?" · "+this.host._t("space.used_by",{list:e.used_by.join(", ")}):""} -
    - - -
    `;var s})} -
    `}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const e="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const s="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let i=null;if("file"===t.source&&t.planFile){i={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:s,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!i&&"file"===t.source&&t.planUrl&&!o&&this.host._aspectJob&&(o=await this.host._aspectJob||null);const a=this.host._serverCfg;let n;if("create"===t.mode)n=no(s,t.title.trim()),a.spaces.push(n);else{if(n=a.spaces.find(t=>t.id===s),!n)throw new Error("space "+s+" is gone from the config");n.title=t.title.trim()}i?(n.plan_url=i.url,n.plan_aspect=i.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==n.plan_url&&(n.plan_url=t.planUrl,n.plan_aspect=o),"draw"===t.source&&(n.plan_url=null,n.plan_aspect=null,delete n.plan_x,delete n.plan_y,delete n.plan_scale,delete n.plan_scale_x,delete n.plan_scale_y,delete n.plan_angle),n.settings={...n.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):Bs,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):Os,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},n.zero_wall_style=t.zeroWallStyle,n.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(ah,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(n.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length?this._openNextImport():e||this.host._importTotal>0?(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._showToast(this.host._t(e&&!this.host._importTotal?"toast.space_added_onboard":"import.done"))):(this.host._showToast("create"===t.mode?this.host._t("toast.space_added"):this.host._t("toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?this._setMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this._primeDrawWallField(),this.host._saveNav())))}catch(t){"conflict"!==t?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this.host._serverCfg;if(!e)return;const s=e.spaces.find(e=>e.id===t.spaceId),i=ro(e,this.host._layout||{},t.spaceId||""),o=1===e.spaces.length&&e.spaces[0]?.id===t.spaceId;if(i.count&&!o)return void(this.host._spaceDialog={...t,deleteBlockers:i.count});if(!s)return;const a=t.spaceId,n=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:lo(this.host._t("confirm.delete_space_body"),this.host._t("confirm.delete_space_vac_routes"),i.routeCount),objectName:s.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),r=this.host._spaceDialog,l=this.host._serverCfg;if(!n||!r||"edit"!==r.mode||r.busy||r.spaceId!==a||!l)return;if(!l.spaces.find(t=>t.id===a))return;const h=ro(l,this.host._layout||{},a),c=1===l.spaces.length&&l.spaces[0]?.id===a;if(!h.count||c){this.host._spaceDialog={...r,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:a,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(e,s),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===a&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=ro(e,this.host._layout||{},a),s=1===e.spaces.length&&e.spaces[0]?.id===a;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...r,deleteBlockers:h.count}}async _saveConfigNow(t=null){this.host._cfgEpoch++;try{await this._writeConfig(t)}catch(t){throw t?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===t?.code&&await this.host._reloadConfigOnly(),t}}_startImport(){const t=this.host._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,e.length?(this.host._importQueue=e,this.host._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();void 0!==t&&(this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...io(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:js,roomOpacity:Ws,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:Bs,tempMax:Os,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:Gs(this.host._imperial),cellCmInput:Ls(Gs(this.host._imperial),this.host._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this._setMode("plan"),this.host._showToast(this.host._t("import.done")))}_renderImportDialog(){const t=this.host._importDialog,e=t.floors.filter(t=>t.checked).length;return p`this.host._importDialog=null}> -
    -
    ${this.host._t("import.hint")}
    - ${t.floors.map((e,s)=>p``)} -
    -
    - - - -
    -
    `}_supportPatch(t,e){const s=this.host._supportDialog;if(!s||s.draftId!==t)return null;const i={...s,...e};return this.host._supportDialog=i,i}async _focusSupport(t){await this.host.updateComplete,this.host.renderRoot.querySelector(t)?.focus({preventScroll:!0})}_updateSupportDraft(t,e){const s=this.host._supportDialog;if(!s||"building"===s.status||"sending"===s.status||"success"===s.status)return;const i={...s,[t]:e},o=Al({...i,attach:!1,preview:null});"message_required"===o&&"validation.message_required"===s.errorCode||"message_too_long"===o||"contact_too_long"===o?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:`validation.${o}`}):i.attach&&i.preview&&i.preview.expiresAt<=Date.now()?this._supportPatch(s.draftId,{[t]:e,status:"error",errorCode:"support_preview_expired"}):this._supportPatch(s.draftId,{[t]:e,status:i.attach&&i.preview?"ready":"idle",errorCode:""})}async _discardSupportPreview(t){if(t)try{await this.host.hass.callWS({type:"houseplan/support/preview/discard",token:t})}catch{}}async _closeSupportDialog(){const t=this.host._supportDialog;if(t){if("building"===t.status||"sending"===t.status){const t=l(this.host.hass,this.host._config?.language);if(!await this.host._confirmDanger({key:"close-support-busy",kind:"warning",title:vo(t,"support.close_busy_title"),message:vo(t,"support.close_busy_body"),confirmLabel:this.host._t("btn.close"),cancelLabel:this.host._t("btn.cancel")}))return await this.host.updateComplete,void this.host.renderRoot.querySelector("#support-dialog")?.rejectClose()}clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this.host._supportDialog=null,t.preview?.token&&"success"!==t.status&&this._discardSupportPreview(t.preview.token)}}_scheduleSupportExpiry(t,e){clearTimeout(this._supportExpiryTimer);const s=Math.max(0,e.expiresAt-Date.now());this._supportExpiryTimer=window.setTimeout(()=>{const s=this.host._supportDialog;s&&s.draftId===t&&s.preview?.token===e.token&&s.attach&&"success"!==s.status&&"sending"!==s.status&&this._supportPatch(t,{status:"error",errorCode:"support_preview_expired"})},Math.min(s+20,2147483647))}_supportFacts(){const t=this.host._haRegistry;return Nl({userAgent:globalThis.navigator?.userAgent||"",language:l(this.host.hass,this.host._config?.language),registryAccess:t.access,registryLastSuccess:t.lastSuccess})}_supportPreviewRequestIsCurrent(t,e){const s=this.host._supportDialog;return e===this._supportPreviewGeneration&&s?.draftId===t&&s.attach}async _buildSupportPreview(t){const e=this.host._supportDialog;if(!e||e.draftId!==t||!e.attach||!Il(this.host._haSupportApi))return;const s=++this._supportPreviewGeneration;this._supportPatch(t,{status:"building",errorCode:""});let i="";try{const e=await this.host.hass.callWS({type:"houseplan/support/preview",card_version:eh,...this._supportFacts(),draft_id:t}),o=e&&"object"==typeof e?e:{},a=Date.now(),n="string"==typeof o.text?o.text:"",r=Number(o.size),l=Number(o.expires_in),h=Number(o.spaces),c=String(o.token||"");/^[0-9a-f]{48}$/.test(c)&&(i=c);const d=String(o.sha256||""),p=Number(o.version),u=String(o.format||""),_=(new TextEncoder).encode(n).byteLength;if(!/^[0-9a-f]{48}$/.test(c)||!/^[0-9a-f]{64}$/.test(d)||"houseplan-support-package"!==u||1!==p||!Number.isInteger(r)||r!==_||!Number.isInteger(h)||h<0||!Number.isFinite(l)||l<=0||l>600||!n.endsWith("\n"))throw{code:"support_rejected"};const m={token:c,expiresAt:a+1e3*l,size:r,sha256:d,spaces:h,format:u,version:p,text:n,preparedAt:a};if(!this._supportPreviewRequestIsCurrent(t,s))return i="",void this._discardSupportPreview(c);if(!this._supportPatch(t,{status:"ready",preview:m,errorCode:"",rawOpen:!1}))return i="",void this._discardSupportPreview(c);i="",this._scheduleSupportExpiry(t,m)}catch(e){if(i){const t=i;i="",this._discardSupportPreview(t)}if(!this._supportPreviewRequestIsCurrent(t,s))return;if(!this._supportPatch(t,{status:"error",errorCode:Hl(e)}))return;this._focusSupport("#support-error")}}async _setSupportAttachment(t){const e=this.host._supportDialog;if(!e||"sending"===e.status||"success"===e.status)return;const s=e.preview?.token||"";if(!t)return this._supportPreviewGeneration+=1,clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,this._supportPatch(e.draftId,{attach:!1,status:"idle",preview:null,rawOpen:!1,errorCode:""}),void(s&&this._discardSupportPreview(s));this._supportPatch(e.draftId,{attach:!0,errorCode:""}),await this._buildSupportPreview(e.draftId)}async _refreshSupportPreview(){const t=this.host._supportDialog;t&&t.attach&&"building"!==t.status&&"sending"!==t.status&&await this._buildSupportPreview(t.draftId)}_downloadSupportPreview(){const t=this.host._supportDialog?.preview;if(!t)return;const e=new Blob([t.text],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(e),i=document.createElement("a");i.href=s,i.download=`houseplan-support-${t.sha256.slice(0,12)}.json`,i.style.display="none",document.body.append(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0)}async _copySupportText(t,e){const s=l(this.host.hass,this.host._config?.language);try{await navigator.clipboard.writeText(t),this.host._showToast(vo(s,e))}catch{this.host._showToast(vo(s,"support.copy_failed"))}}async _submitSupport(){const t=this.host._supportDialog;if(!t||!Il(this.host._haSupportApi))return;const e=Al(t);if(e)return this._supportPatch(t.draftId,{status:"error",errorCode:`validation.${e}`}),void this._focusSupport("#support-message");if(!Fl(t))return;const s=Rl(t),i=this._supportPatch(t.draftId,{status:"sending",errorCode:"",idempotencyKey:s.idempotencyKey,submissionFingerprint:s.fingerprint});if(i)try{const e=await this.host.hass.callWS({type:"houseplan/support/submit",message:i.message.trim(),contact:i.contact.trim(),...i.attach&&i.preview?{preview_token:i.preview.token}:{},idempotency_key:i.idempotencyKey}),s=e&&"object"==typeof e&&"report_id"in e?String(e.report_id||""):"";if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(s))throw{code:"support_unavailable"};if(clearTimeout(this._supportExpiryTimer),this._supportExpiryTimer=void 0,!this._supportPatch(t.draftId,{status:"success",reportId:s,errorCode:""}))return;this._focusSupport("#support-receipt")}catch(e){if(!this._supportPatch(t.draftId,{status:"error",errorCode:Hl(e)}))return;this._focusSupport("#support-error")}}_supportErrorText(t){const e=l(this.host.hass,this.host._config?.language);if(t.errorCode.startsWith("validation.")){return vo(e,`support.validation.${t.errorCode.slice(11)}`)}return vo(e,`support.error.${t.errorCode||"support_unavailable"}`)}_supportMessageKeydown(t){"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this._submitSupport())}_renderSupportDialog(){const t=this.host._supportDialog,e=l(this.host.hass,this.host._config?.language),s="ru"===e?"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.ru.md":"https://github.com/Matysh/houseplan-card/blob/main/docs/USER-GUIDE.md",i=Il(this.host._haSupportApi),o=(t,s)=>vo(e,t,s),a="building"===t.status||"sending"===t.status,n=Al(t),r=t.preview?Math.max(0,Math.floor((Date.now()-t.preview.preparedAt)/6e4)):0,h="error"===t.status&&!t.errorCode.startsWith("validation."),c="validation.contact_too_long"===t.errorCode,u="validation.message_required"===t.errorCode||"validation.message_too_long"===t.errorCode;return p`{this._closeSupportDialog()}}> -
    -
    -

    ${o("support.about_group")}

    -
    ${this.host._t("gs.about_version",{v:eh})}
    - -
    -
    -

    ${o("support.guide_group")}

    - - ${o("support.guide")} -
    - ${i?p` -
    -

    ${o("support.form_group")}

    - - this._updateSupportDraft("contact",t.target.value)} /> - - - -

    ${o("support.attach_hint")}

    - ${t.attach?p`

    - - ${o("support.geometry_warning")} -

    `:d} - ${"building"===t.status?p`
    - ${o("support.building")} -
    `:d} - ${t.attach&&t.preview?p` -
    -
    ${o("support.preview_summary",{version:t.preview.version,spaces:t.preview.spaces,size:(_=t.preview.size,(Math.max(0,_)/1024).toFixed(_<10240?1:0))})}
    -
    SHA-256${t.preview.sha256}
    -
    ${o("support.prepared",{n:r})}
    -
    this._supportPatch(t.draftId,{rawOpen:e.currentTarget.open})}> - ${o("support.show_data")} - ${t.rawOpen?p``:d} -
    -
    - - -
    -
    `:d} -

    ${o("support.privacy")}

    - ${"error"===t.status?p` - `:d} - ${"error"===t.status&&t.attach&&!t.preview?p` -
    - -
    `:d} - ${h?p` -
    - ${o("support.manual_recovery")} -
    - - ${t.preview?p``:d} - Telegram - GitHub -
    -
    `:d} - ${"success"===t.status?p` -
    - ${o("support.success",{id:t.reportId})} - -
    `:d} -
    `:p` -
    - - ${o("support.update_required")} -
    `} -
    -
    - - - ${i&&"success"!==t.status?p` - `:d} -
    -
    `;var _}_preflightDiagnostics(t,e){const s=new Map((e?.spaces||[]).map(t=>[String(t?.id||""),t]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:eh,checkedAt:(new Date).toISOString(),preflightFingerprint:t.fingerprint,failures:t.failures.map(t=>({spaceId:t.spaceId,displayName:t.displayName,reason:t.reason,detail:t.detail??null,spaceGeometryFingerprint:s.has(t.spaceId)?me(s.get(t.spaceId)):null}))}}_reportPreflightFailure(t,e){t.ok||t.fingerprint===this.host._reportedPreflightFingerprint||(this.host._reportedPreflightFingerprint=t.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(t,e)))}_preflightVersionsDiffer(){const t=this.host._haIntegrationVersion;return"string"==typeof t&&t.length>0&&t!==eh}async _copyPreflightDiagnostics(){const t=this.host._alignDialog?.preflight;if(!t||t.ok)return;const e=JSON.stringify(this._preflightDiagnostics(t,this.host._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(e),this.host._preflightClipboardFallback=null,this.host._showToast(this.host._t("gs.preflight_copied"))}catch{this.host._preflightClipboardFallback=e}}_checkOptimizeGeometryImpl(t){return qs(t,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)})})}_checkSpacePhysicalGeometryImpl(t,e,s){return Us(t,e,{fallbackSpaceName:t=>this.host._t("gs.align_preflight_space",{n:String(t)}),captureWallGeometry:s?(t,e)=>s(e):void 0})}_optimizeReferenceContext(t){const e=this.host._haRegistry,s=this.host._fullRegistryHass,i={},o=(...t)=>{for(const e of t){const t=String(e||"").trim();if(t)return t}return""};for(const[t,s]of Object.entries(e.devices||{}))i[t]=o(s?.name_by_user,s?.name,s?.model);for(const[t,s]of Object.entries(e.entities||{}))i[`lg_${t}`]=o(this.host.hass?.states?.[t]?.attributes?.friendly_name,s?.name,s?.original_name);for(const t of this.host._devices)i[t.id]=o(t.name,i[t.id]);for(const t of this.host._serverCfg?.markers||[]){const e=String(t.binding||"").indexOf(":"),a=e>0?t.binding.slice(0,e):"",n=e>0?t.binding.slice(e+1):"",r="device"===a?s?.devices?.[n]:"entity"===a?s?.entities?.[n]:null;i[t.id]=o(t.name,"device"===a?r?.name_by_user:null,r?.name,"entity"===a?this.host.hass?.states?.[n]?.attributes?.friendly_name:null,i[t.id])}return{effectiveAreaByMarker:Object.fromEntries(this.host._devices.filter(t=>!t.virtual&&!!t.area).map(t=>[t.id,t.area])),ownerRoster:{authoritative:e.authoritative,deviceIds:Object.keys(e.devices||{}),entityIds:[...new Set([...Object.keys(e.entities||{}),...Object.keys(this.host.hass?.states||{})])],names:i},removeLiveMissingPositions:t}}_previewAlignDialog(t){if(!this.host._norm||!this.host._serverCfg)return;const e=this.host._serverCfg.spaces||[];let s;try{s=Ml(this.host._serverCfg,this.host._layout||{},this._optimizeReferenceContext(t))}catch(t){return void this._showWallModelMigrationBlocked(t)}const i=s.changed?this.host._checkOptimizeGeometry(s.config):null;i&&this._reportPreflightFailure(i,s.config);const o=Math.ceil(10*s.report.maxShiftCm)/10,a=e.find(t=>null!=t?.id&&String(t.id)===s.report.maxSpace),n=e.length>1&&a?String(a.title||a.id):"";this.host._preflightClipboardFallback=null,this.host._alignDialog={report:s.report,config:s.config,layout:s.layout,cm:o,where:n,preflight:i,changed:s.changed,busy:!1,removeLiveMissingPositions:t}}async _runAlignToGrid(){let t=this.host._alignDialog;if(!t||t.busy||!this.host._serverCfg||!t.changed||!t.preflight?.ok)return;const e=_(t.config);if(t.preflight.fingerprint!==e){const e=this.host._checkOptimizeGeometry(t.config);if(this._reportPreflightFailure(e,t.config),t={...t,preflight:e},this.host._alignDialog=t,!e.ok)return}this._clearGeometryGesture(),this.host._alignDialog={...t,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain;const e=await this.host.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});this.host._serverCfg=t.config,this.host._layout=t.layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._cfgRev=e?.config_rev??this.host._cfgRev+1,this.host._layoutRev=e?.layout_rev??this.host._layoutRev+1,this.host._canOptimizeUndo=!!e?.can_undo,this.host._undoKind=e?.can_undo?"optimize":null,this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host._alignDialog=null,this.host._preflightClipboardFallback=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.coordsCanonicalized+t.report.latticeCoordinatesCanonicalized+t.report.wallsMerged+t.report.spansMerged+t.report.partitionsMerged+t.report.partitionsReconciled+t.report.openingsRehosted+t.report.wallsStraightened),r:String(t.report.spaceRefsRemapped+t.report.roomRefsRemapped+t.report.positionsRemapped+t.report.markersDetached+t.report.orphanRoomLabelsRemoved+t.report.orphanDevicePositionsRemoved+t.report.orphanGroupPositionsRemoved)}))}catch(t){if(this.host._alignDialog&&(this.host._alignDialog={...this.host._alignDialog,busy:!1}),"wall_model_client_outdated"===t?.code)return void this.host._showToast(this.host._t("toast.wall_model_client_outdated"));"conflict"===t?.code&&await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _undoPlanOptimization(){if(!this.host._canOptimizeUndo||this.host._optimizeUndoBusy)return;const t=this.host._undoKind;this._clearGeometryGesture(),this.host._optimizeUndoBusy=!0,this.host.requestUpdate();try{await this.host.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev});const[e,s]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._serverCfg=e?.config||this.host._serverCfg,this.host._cfgRev=e?.rev??this.host._cfgRev,this.host._layout=s?.layout||this.host._layout,this.host._geometryHistory.clear(),this.host._cancelDeviceDrag(),this.host._devicePositionHistory.clear(),this.host._layoutRev=s?.rev??this.host._layoutRev,this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._cacheSnapshot(),this.host.requestUpdate(),this.host._showToast(this.host._t("import"===t?"backup.import_undone":"gs.optimize_undone"))}catch(t){this.host._canOptimizeUndo=!1,this.host._undoKind=null,this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}finally{this.host._optimizeUndoBusy=!1,this.host.requestUpdate()}}async _runBackupExport(){const t=this.host._backupExportDialog;if(t&&!t.busy){this.host._backupExportDialog={...t,busy:!0,error:""};try{const e=await this.host.hass.callWS({type:"houseplan/export/create",kind:t.kind,space_id:"space"===t.kind?this.host._space:void 0,..."space"===t.kind&&t.planOnly?{plan_only:!0}:{},card_version:eh}),s=new Blob([JSON.stringify(e.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),i=URL.createObjectURL(s),o=document.createElement("a");o.href=i,o.download=e.filename||`houseplan-${t.kind}.json`,o.style.display="none",document.body.append(o),o.click(),o.remove(),window.setTimeout(()=>URL.revokeObjectURL(i),0),this.host._backupExportDialog=null,this.host._showToast(this.host._t("backup.export_done"))}catch(t){this.host._backupExportDialog&&(this.host._backupExportDialog={...this.host._backupExportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _pickBackupImport(t){const e=t.target,s=e.files?.[0];if(e.value="",s){this.host._settingsDialog=null,this.host._backupImportDialog={filename:s.name,size:s.size,token:"",preview:null,expectedConfigRev:this.host._cfgRev,expectedLayoutRev:this.host._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),await this.host._writeChain,this.host._persistLayout.pending()&&this.host._persistLayout.flush();const t="/api/houseplan/import/preview?duplicate_policy=skip",e={method:"POST",body:s,headers:{"content-type":"application/json"}},i=this.host.hass?.fetchWithAuth?await this.host.hass.fetchWithAuth(t,e):await fetch(t,{...e,headers:{...e.headers,...this.host.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.host.hass.auth.data.access_token}`}:{}}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error)throw Object.assign(new Error(o.message||o.error||`HTTP ${i.status}`),{code:o.error});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,token:o.token,preview:o.preview,expectedConfigRev:o.expected_config_rev,expectedLayoutRev:o.expected_layout_rev,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}}async _setBackupDuplicatePolicy(t){const e=this.host._backupImportDialog;if(e&&!e.busy&&e.token&&t!==e.duplicatePolicy){this.host._backupImportDialog={...e,duplicatePolicy:t,busy:!0,error:""};try{const s=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:t});this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,preview:{...e.preview,...s.preview},expectedConfigRev:s.expected_config_rev,expectedLayoutRev:s.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(t){this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,duplicatePolicy:e.duplicatePolicy,preview:e.preview,confirmMissing:e.confirmMissing,busy:!1,error:this._backupErrorText(t)})}}}async _applyBackupImport(){const t=this.host._backupImportDialog;if(!t||t.busy||!t.token||!t.preview)return;if(t.preview.confirmation_required&&!t.confirmMissing)return;this.host._backupImportDialog={...t,busy:!0,error:""};const e=this.host._space;try{const s=await this.host.hass.callWS({type:"houseplan/import/apply",token:t.token,expected_config_rev:t.expectedConfigRev,expected_layout_rev:t.expectedLayoutRev,duplicate_policy:t.duplicatePolicy,confirm_missing_content:t.confirmMissing}),[i,o]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(i,o),this.host._geometryHistory.clear(),this.host._dirtyPos.clear(),this.host._sentPos.clear(),this.host._defPos={},this.host._cfgEpoch++,this.host._modelCache=null,this.host._frame=null,this.host._visibleDeviceSnapshot=null,this.host._candidateDeviceSnapshot=null,this.host._stagedDeviceSnapshotToken=-1,this.host._capturedSnapshotConfigEpoch=-1,this.host._regSignature="",this.host._signer.invalidate(this.host.hass),this.host._resign(),this.host._maybeRebuildDevices();const a=this.host._serverCfg?.spaces||[],n="space"===s.kind&&s.space_id?s.space_id:a.some(t=>t.id===e)?e:a[0]?.id||this.host._space;this.host._hasFixedFloor?this.host._adoptInitialSpace(this.host._model,!0):this.host._commitSpace(n),this.host._backupImportDialog=null,this.host._cacheSnapshot(),this.host.requestUpdate();const r="space"===s.kind?t.preview.counts:s.counts;this.host._showToast(this.host._t("space"===s.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(s.repaired_target_refs||0)}))}catch(t){if("conflict"===t?.code&&this.host._backupImportDialog?.token)try{const t=this.host._backupImportDialog,e=await this.host.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:t.duplicatePolicy});return void(this.host._backupImportDialog={...t,preview:{...t.preview,...e.preview},expectedConfigRev:e.expected_config_rev,expectedLayoutRev:e.expected_layout_rev,confirmMissing:!1,busy:!1,error:this.host._t("backup.revalidated")})}catch(e){t=e}this.host._backupImportDialog&&(this.host._backupImportDialog={...this.host._backupImportDialog,busy:!1,error:this._backupErrorText(t)})}}_renderBackupExportDialog(){const t=this.host._backupExportDialog,e=(this.host._serverCfg?.spaces||[]).find(t=>t.id===this.host._space);return p`this.host._backupExportDialog=null}> -
    -
    ${this.host._t("backup.export_hint")}
    - - - ${"space"===t.kind&&e?p``:d} -
    ${this.host._t("backup.privacy_warning")}
    - ${t.error?p``:d} -
    -
    - - - -
    -
    `}_renderBackupImportDialog(){const t=this.host._backupImportDialog,e=t.preview,s=(e?.content||[]).filter(t=>"decor_asset"===t.kind),i=new Set(s.map(t=>t.url)).size,o=s.filter(t=>"missing_preserved"===t.state),a=new Set(o.map(t=>t.url)).size,n=e?.counts||{},r=e?.reference_report||{},l=t=>Object.values(t||{}).reduce((t,e)=>t+(Number(e)||0),0),h=[["incoming_remapped",l(r.remapped?.incoming)],["target_repaired",l(r.remapped?.target)],["preserved_unresolved",l(r.preservedUnresolved)],["collisions",l(r.collisions)],["dropped_links",l(r.droppedIncomingLinks)],["bounded_lineages",Number(r.boundedLineages)||0]].filter(([,t])=>t>0);return p`this.host._backupImportDialog=null}> -
    -
    ${t.filename}${(t.size/1024).toFixed(1)} KB
    - ${t.busy&&!e?p`
    ${this.host._t("backup.reading")}
    `:d} - ${t.error?p``:d} - ${e?p` -
    - ${this.host._t("full"===e.kind?"backup.full":"backup.current_space")} - ${e.plan_only?p`${this.host._t("backup.plan_only_preview")}`:d} - ${this.host._t("same"===e.source?"backup.same_source":"backup.foreign_source")} - ${this.host._t("backup.created",{value:e.created_at||"—"})} - ${this.host._t("backup.versions",{card:e.card_version||"—",integration:e.integration_version||"—",model:String(e.model_version??"—")})} -
    -
    - ${this.host._t("backup.count_spaces",{n:"full"===e.kind?`${e.current_counts?.spaces||0} → ${n.spaces||0}`:String(n.spaces||0)})} - ${this.host._t("backup.count_rooms",{n:"full"===e.kind?`${e.current_counts?.rooms||0} → ${n.rooms||0}`:String(n.rooms||0)})} - ${this.host._t("backup.count_walls",{n:"full"===e.kind?`${e.current_counts?.walls||0} → ${n.walls||0}`:String(n.walls||0)})} - ${this.host._t("backup.count_openings",{n:"full"===e.kind?`${e.current_counts?.openings||0} → ${n.openings||0}`:String(n.openings||0)})} - ${this.host._t("backup.count_decor",{n:"full"===e.kind?`${e.current_counts?.decor||0} → ${n.decor||0}`:String(n.decor||0)})} - ${this.host._t("backup.count_markers",{n:"full"===e.kind?`${e.current_counts?.markers||0} → ${n.markers||0}`:String(n.markers||0)})} - ${this.host._t("backup.count_layout",{n:"full"===e.kind?`${e.current_counts?.layout||0} → ${n.layout||0}`:String(n.layout||0)})} -
    - ${e.bindings?p`
    ${this.host._t("backup.bindings",{device:String(e.bindings.device||0),entity:String(e.bindings.entity||0),virtual:String(e.bindings.virtual||0),legacy:String(e.legacy_positions||0)})}
    ${this.host._t("backup.binding_status",{active:String(e.bindings.active||0),disabled:String(e.bindings.disabled||0),missing:String(e.bindings.missing||0)})}
    `:d} - ${e.missing_areas?.length?p`
    ${this.host._t("backup.missing_areas",{areas:e.missing_areas.join(", ")})}
    `:d} - ${e.dropped_marker_links?p`
    ${this.host._t("backup.dropped_marker_links",{n:String(e.dropped_marker_links)})}
    `:d} - ${e.repaired_target_refs?p`
    ${this.host._t("backup.repaired_target_refs",{n:String(e.repaired_target_refs)})}
    `:d} - ${e.preserved_unresolved_refs?p` -
    ${this.host._t("backup.preserved_unresolved_refs",{n:String(e.preserved_unresolved_refs)})}
    ${this.host._t("backup.preserved_unresolved_hint")}
    - `:d} - ${h.length?p`
    - ${this.host._t("backup.import_details")} -
    - ${h.map(([t,e])=>p`${this.host._t(`backup.import_detail.${t}`,{n:String(e)})}`)} - ${(r.examples||[]).slice(0,8).map(t=>p` - ${t.owner} → ${t.reference} - `)} -
    -
    `:d} - ${"full"===e.kind?p` -
    ${this.host._t("backup.replace_warning")}
    - ${"foreign"===e.source?p`
    ${this.host._t("backup.foreign_bookkeeping")}
    `:d}`:p` -
    ${this.host._t("backup.final_name")}${e.space_title}
    -
    ${this.host._t("backup.target_settings")}
    - ${e.duplicates?p`
    ${this.host._t("backup.duplicates",{n:String(e.duplicates)})} - - -
    `:d}`} - ${e.content?.length?p`
    - ${this.host._t("backup.content")} - ${s.length?p`${this.host._t("backup.decor_images_summary",{assets:i,objects:s.length,missing:a})}`:d} - ${e.content.filter(t=>"decor_asset"!==t.kind).map(t=>p`${t.url} · ${this.host._t("available"===t.state?"backup.content_available":"external"===t.state?"backup.content_external":"backup.content_detach_required")}`)} -
    `:d} - ${e.confirmation_required?p``:d} - `:d} -
    -
    - - - ${e?p``:d} -
    -
    `}_setFillColor(t,e){const s=this.host._settingsDialog;this.host._settingsDialog={...s,colors:{...s.colors,[t]:{...s.colors[t],...e}}}}_updateDecorStyle(t){this.host._decorStyle=t,null!==this._decorStylePersistTimer&&window.clearTimeout(this._decorStylePersistTimer),this._decorStylePersistTimer=window.setTimeout(()=>{this._decorStylePersistTimer=null,this._persistDecorStyle()},1e3)}_persistDecorStyle(){const t=this.host._serverCfg;if(!t)return;const e=Ks(this.host._decorStyle),s={...t.settings},i=JSON.stringify(s.decor_default_style??null);e?s.decor_default_style=e:delete s.decor_default_style,JSON.stringify(s.decor_default_style??null)!==i&&(this.host._serverCfg={...t,settings:s},this._saveConfig())}async _saveSettingsDialog(){const t=this.host._settingsDialog;if(!t||t.busy)return;this.host._settingsDialog={...t,busy:!0};let e=null;try{const s=this.host._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Vs);let o={...s.settings};i?delete o.fill_colors:o.fill_colors=t.colors;const a=this.host._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(a)&&a>0&&300!==Math.round(a)?o.glow_radius_cm=Math.round(a):delete o.glow_radius_cm,t.bgColor?o.bg_color=t.bgColor:delete o.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?o.north_deg=t.northDeg:delete o.north_deg,o.bg_mode=t.bgMode,t.sunRays?o.sun_rays=!0:delete o.sun_rays,t.showRoomTooltip?delete o.show_room_tooltip:o.show_room_tooltip=!1,o=Js(o,t.zigbeeTopology),delete o.weather_entity;const n={...s,settings:o};e=u(s,n,this.host._cfgContentFingerprint,this.host._cfgRev,_),this.host._serverCfg=n,await this._saveConfigNow(e),!t.showRoomTooltip&&this.host._tip?.room&&(this.host._tip=null),this.host._settingsDialog=null,this.host.requestUpdate(),this.host._showToast(this.host._t("gs.saved"))}catch(t){e&&m(this.host,e,_),this.host._settingsDialog&&(this.host._settingsDialog={...this.host._settingsDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_boolInput(t,e,s=!1){const i=t=>e(!!t.target.checked);return customElements.get("ha-switch")?p``:p``}_rangeInput(t,e,s,i,o,a=!1,n){const r=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?p``:p``}_renderColorRow(t,e){const s=this.host._settingsDialog.colors[t];return p`
    - {this._setFillColor(t,{c:e.detail.color,a:e.detail.opacity})}}> -
    `}_renderAlignDialog(){const t=this.host._alignDialog,e=t.report,s=t.changed&&!t.preflight?.ok,i=t.preflight?.failures||[],o=i.slice(0,3).map(t=>t.displayName),a=o.length?o.join(", "):this.host._t("gs.align_preflight_space",{n:"1"}),n=Math.max(0,i.length-o.length),r=n?this.host._t("gs.align_preflight_more",{n:String(n)}):"",l=e.spaceRefsRemapped+e.roomRefsRemapped+e.positionsRemapped+e.markersDetached,h=e.migrated+e.canonicalized+e.coordsCanonicalized+e.wallSegmentsMigrated+e.wallsMerged+e.spansMerged+e.partitionsMerged+e.partitionsReconciled+e.openingsRehosted+e.redundantDraftsRemoved,c=e.moved+e.rotated+e.removedDrafts+e.coordsCanonicalized+e.wallsStraightened,u=Math.ceil(10*e.maxStraightenShiftCm)/10,_=(this.host._serverCfg?.spaces||[]).find(t=>String(t?.id||"")===e.maxStraightenSpace),m=(this.host._serverCfg?.spaces||[]).length>1&&_?String(_.title||_.id):"",g=e.orphanRoomLabelsRemoved+e.orphanDevicePositionsRemoved+e.orphanGroupPositionsRemoved,f=e.liveMissingPositions.map(t=>t.name).filter(Boolean),b=f.slice(0,3).join(", "),v=Math.max(0,f.length-3),y=b?this.host._t("gs.optimize_live_names",{names:b,more:v?this.host._t("gs.optimize_reference_more",{n:String(v)}):""}):"",$=e.unverifiedPositions.some(t=>"registry_unavailable"===t.reason),w=t=>e.removedPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_removed"):e.liveMissingPositions.some(e=>e.id===t.id)?this.host._t("gs.optimize_detail_live"):this.host._t("gs.optimize_detail_unverified"),k=t=>this.host._t("room_label"===t?"gs.optimize_detail_room_label":"group"===t?"gs.optimize_detail_group":"device"===t?"gs.optimize_detail_device":"gs.optimize_detail_unknown"),x=[...e.removedPositions,...e.liveMissingPositions.filter(t=>!e.removedPositions.some(e=>e.id===t.id)),...e.unverifiedPositions],S=x.slice(0,10),M=Math.max(0,x.length-S.length);return p`{this.host._alignDialog=null,this.host._preflightClipboardFallback=null}}> -
    - ${s?p` -

    ${this.host._t("gs.align_preflight_failed",{spaces:a,more:r})}

    - ${i.slice(0,10).map(t=>p`

    - ${t.displayName}: ${this.host._t(`gs.preflight_reason_${t.reason}`)} -

    `)} - ${i.length>10?p`

    - ${this.host._t("gs.align_preflight_more",{n:String(i.length-10)})} -

    `:d} -
    ${this.host._t("gs.align_preflight_hint")}
    - ${this._preflightVersionsDiffer()?p` -
    ${this.host._t("gs.preflight_update_hint")}
    `:d} -
    - -
    - ${this.host._preflightClipboardFallback?p`
    - ${this.host._t("gs.preflight_copy")} -
    ${this.host._preflightClipboardFallback}
    -
    `:d}`:t.changed?p` - ${e.moved?p`

    ${this.host._t("gs.align_count",{n:String(e.moved),total:String(e.total),cm:String(t.cm)})}

    `:d} - ${e.latticeCoordinatesCanonicalized?p` -

    ${this.host._t("gs.optimize_lattice_summary",{n:String(e.latticeCoordinatesCanonicalized),cm:Ys(e.latticeMaxShiftCm)})}

    - ${e.latticeSpaces.map(t=>p`

    ${this.host._t("gs.optimize_lattice_space",{space:t.space,n:String(t.canonicalized),far:String(t.far)})}

    `)} - `:d} - ${t.where?p`

    ${this.host._t("gs.align_where",{s:t.where})}

    `:d} - ${e.rotated?p`

    ${this.host._t("gs.align_turned",{n:String(e.rotated)})}

    `:d} - ${e.removedDrafts?p`

    ${this.host._t("gs.align_removed_drafts",{n:String(e.removedDrafts)})}

    `:d} - ${e.redundantDraftsRemoved?p`

    ${this.host._t("gs.optimize_redundant_drafts",{n:String(e.redundantDraftsRemoved)})}

    `:d} - ${e.wallSegmentsMigrated?p`

    ${this.host._t("gs.wall_segments_migrated",{n:String(e.wallSegmentsMigrated)})}

    `:d} - ${e.legacyZeroWallsMigrated?p`

    ${this.host._t("gs.zero_walls_migrated",{n:String(e.legacyZeroWallsMigrated)})}

    `:d} - ${h?p`

    ${this.host._t("gs.optimize_changes",{m:String(e.migrated),c:String(e.canonicalized),p:String(e.coordsCanonicalized),w:String(e.wallsMerged),s:String(e.spansMerged),i:String(e.partitionsMerged)})}

    `:d} - ${e.partitionsReconciled?p`

    ${this.host._t("gs.optimize_coincident_partitions",{n:String(e.partitionsReconciled)})}

    `:d} - ${e.openingsRehosted?p`

    ${this.host._t("gs.optimize_openings_rehosted",{n:String(e.openingsRehosted)})}

    `:d} - ${e.wallsStraightened?p`

    ${this.host._t("gs.optimize_walls_straightened",{n:String(e.wallsStraightened),cm:String(u)})}

    `:d} - ${m?p`

    ${this.host._t("gs.optimize_walls_straightened_where",{s:m})}

    `:d} - ${e.glowSpacesMigrated||e.glowRoomsMigrated?p`

    ${this.host._t("gs.optimize_glow_migration",{spaces:String(e.glowSpacesMigrated),rooms:String(e.glowRoomsMigrated)})}

    `:d} - ${c?p`
    ${this.host._t("gs.align_warn")}
    `:d}`:p`

    ${this.host._t(e.liveMissingPositions.length||e.unverifiedPositions.length||e.nestedRefsUnresolved?"gs.optimize_no_automatic_changes":"gs.align_none")}

    `} - ${!s&&e.wallsStraightenSkipped?p`

    ${this.host._t("gs.optimize_walls_straighten_skipped",{n:String(e.wallsStraightenSkipped)})}

    `:d} - ${l?p`

    ${this.host._t("gs.optimize_references",{spaces:String(e.spaceRefsRemapped),rooms:String(e.roomRefsRemapped),positions:String(e.positionsRemapped),detached:String(e.markersDetached)})}

    `:d} - ${g?p`

    ${this.host._t("gs.optimize_orphans_removed",{total:String(g),rooms:String(e.orphanRoomLabelsRemoved),devices:String(e.orphanDevicePositionsRemoved),groups:String(e.orphanGroupPositionsRemoved)})}

    `:d} - ${e.liveMissingPositions.length?p`
    -

    ${this.host._t(t.removeLiveMissingPositions?"gs.optimize_live_positions_remove":"gs.optimize_live_positions",{n:String(e.liveMissingPositions.length),names:y})}

    - - ${t.removeLiveMissingPositions?p`
    - ${this.host._t("gs.optimize_live_selected")} -
    `:d} -
    `:d} - ${e.unverifiedPositions.length?p``:d} - ${e.nestedRefsUnresolved?p``:d} - ${x.length?p`
    - ${this.host._t("gs.optimize_details")} -
      - ${S.map(t=>p`
    • ${this.host._t("gs.optimize_detail_item",{status:w(t),kind:k(t.kind),id:t.id,space:t.spaceId})}
    • `)} -
    - ${M?p`
    ${this.host._t("gs.optimize_details_more",{n:String(M)})}
    `:d} -
    `:d} -
    -
    - - - ${t.changed&&t.preflight?.ok?p` - `:d} -
    -
    `}_renderSettingsDialog(){return p`this.host._settingsDialog=null}> -
    -
    ${vo(l(this.host.hass,this.host._config?.language),"gs.hint")}
    - this.host._settingsDialog={...this.host._settingsDialog,zigbeeTopology:t.detail}}> - - ${this._renderColorRow("light_on","gs.light_on")} - ${this._renderColorRow("light_off","gs.light_off")} - ${this._renderColorRow("light_none","gs.light_none")} - - ${this._renderColorRow("temp_cold","gs.temp_cold")} - ${this._renderColorRow("temp_ok","gs.temp_ok")} - ${this._renderColorRow("temp_hot","gs.temp_hot")} - - ${this._renderColorRow("lqi_low","gs.lqi_low")} - ${this._renderColorRow("lqi_high","gs.lqi_high")} - - ${this._renderColorRow("glow_base","gs.glow_base")} - ${this._renderColorRow("glow_light","gs.glow_light")} -
    - - ${this._help("gs.glow_radius.help")} - {const e=nh(t.target.value);null!=e&&e>0&&(this.host._settingsDialog={...this.host._settingsDialog,glowRadius:e})}} /> - ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} -
    - - ${this._renderColorRow("wall_fill","gs.wall_fill")} - -
    - - ${this._help("gs.bg_mode.help")} - -
    - ${"static"===this.host._settingsDialog.bgMode?p`
    - {this.host._settingsDialog={...this.host._settingsDialog,bgColor:t.detail.color}}}> - ${this.host._settingsDialog.bgColor?p``:p`${this.host._t("gs.bg_theme")}`} -
    `:d} - - ${Zs(this.host.hass)?d:p`
    ${this.host._t("gs.sun_missing")}
    `} -
    - ${this.host._renderCompass()} -
    -
    - - ${this._help("gs.north.help")} -
    -
    - {const e=t.target.value.trim(),s=""===e?null:Math.round(Number(e));this.host._settingsDialog={...this.host._settingsDialog,northDeg:null!==s&&Number.isFinite(s)?Math.min(359,Math.max(0,s)):null}}} /> - ${null!==this.host._settingsDialog.northDeg?p``:d} -
    -
    -
    - - ${this.host._canEdit?p` - -
    ${this.host._t("gs.backup_hint")}
    -
    - - - - this._pickBackupImport(t)} /> - - ${this.host._canOptimizeUndo&&"import"===this.host._undoKind?p` - `:d} -
    `:d} - -
    ${this.host._t("gs.grid_hint")}
    -
    - -
    - ${this.host._canOptimizeUndo&&"import"!==this.host._undoKind?p`
    - -
    `:d} -
    -
    - - - - -
    -
    `}_rulesSet(t){this.host._rulesDialog={...this.host._rulesDialog,rules:t}}async _saveRules(){const t=this.host._rulesDialog;if(!t||t.busy)return;const e=t.rules.filter(t=>t.pattern.trim()&&t.icon.trim());this.host._rulesDialog={...t,busy:!0};try{const t=this.host._serverCfg,s=JSON.stringify(e)===JSON.stringify(Qt),i={...t.settings};s?delete i.icon_rules:i.icon_rules=e,this.host._serverCfg={...t,settings:i},await this._saveConfigNow(),this.host._rulesDialog=null,this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("rules.saved"))}catch(t){this.host._rulesDialog&&(this.host._rulesDialog={...this.host._rulesDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}_renderRulesDialog(){const t=this.host._rulesDialog,e=Xs(t.rules),s=t.test.trim()?Qs(t.test,"",e):null,i=(e,s)=>{const i=[...t.rules],o=e+s;o<0||o>=i.length||([i[e],i[o]]=[i[o],i[e]],this._rulesSet(i))};return p`this.host._rulesDialog=null}> -
    -
    ${this.host._t("rules.hint")}
    -
    - this.host._rulesDialog={...t,test:e.target.value}} /> - ${s?p`${s}`:d} -
    - ${t.rules.map((e,s)=>{const o=""!==e.pattern.trim()&&!ti(e.pattern);return p`
    - {const o=[...t.rules];o[s]={...e,pattern:i.target.value},this._rulesSet(o)}} /> - {const o=[...t.rules];o[s]={...e,icon:i.target.value},this._rulesSet(o)}} /> - - i(s,-1)}> - i(s,1)}> - this._rulesSet(t.rules.filter((t,e)=>e!==s))}> -
    `})} - -
    -
    - - - - -
    -
    `}_saveKioskScale(t){this.host._kioskScale={...this.host._kioskScale,...t};try{localStorage.setItem("houseplan_card_kiosk_v1",JSON.stringify(this.host._kioskScale))}catch{}this.host.requestUpdate()}_renderKioskDialog(){const t=this.host._kioskScale,e=(e,s)=>p` -
    - ${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))} - ${Math.round(100*t[e])}% -
    `;return p`this.host._kioskDialog=!1}> -
    -
    ${this.host._t("kiosk.hint")}
    - ${e("icon",this.host._t("kiosk.icon_scale"))} - ${e("font",this.host._t("kiosk.font_scale"))} -
    -
    - - - -
    -
    `}_renderVacSection(t){const e=this.host._devices.find(e=>e.id===t.devId);if(!e||!this.host._isVacDev(e))return d;const s=e.marker?.vacuum||{},o=this.host._vacAllCamerasFor===e.id,a=this.host._vacSourceResolution(e,o),n=a.entityId,r=a.candidates.find(t=>t.entityId===n)||null,l=n?ei(this.host.hass?.states[n]?.attributes):null,h=!1!==s.live&&("ok"===a.status||"unsupported"===a.status),c=this._vacPlanRoomAnchors(e.space),u=l?si(l.rooms,c.map(t=>t.name)):0,_=!!(h&&l&&u>=3),m=t=>{const s=this.host._vacEnsureMarker(e);s&&(s.vacuum={...s.vacuum||{},...t},this.host._regSignature="",this.host._maybeRebuildDevices(),this._saveConfig(),this.host.requestUpdate())},g=new Set(e.entities||[]),f=a.candidates.filter(t=>g.has(t.entityId)||t.entityId===s.source),b=o?a.candidates.filter(t=>t.entityId.startsWith("camera.")&&!f.some(e=>e.entityId===t.entityId)):[],v=t=>[t.hasPosition?this.host._t("vac.cap_position"):"",t.hasRooms?this.host._t("vac.cap_rooms_short"):"",t.hasPath?this.host._t("vac.cap_path"):"",t.hasMapId?this.host._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this.host._t("vac.cap_none"),y=t=>p` - `,$=a.candidates.some(t=>g.has(t.entityId)&&"known_xcme_incomplete"===t.category)||!!a.pinned&&!!n?.startsWith("camera.")&&!r?.hasPosition;return p` - -
    -
    -
    ${this.host._t("vac.diag_source")}${n||this.host._t("vac.source_none")}
    - ${r?.platform?p`
    ${this.host._t("vac.diag_platform")}${r.platform}
    `:d} -
    ${this.host._t("vac.diag_status")}${this.host._t(`vac.source_status_${a.status}`)}
    -
    ${this.host._t("vac.diag_position")}${l?.pos?this.host._t("common.yes"):this.host._t("common.no")}
    -
    ${this.host._t("vac.diag_rooms")}${i(this.host._t("vac.diag_rooms_value"),{total:String(l?.rooms.length||0),matched:String(u),readiness:this.host._t(u>=3?"vac.autocal_ready":"vac.autocal_not_ready")})}
    -
    ${this.host._t("vac.diag_path")}${l?.path.length?this.host._t("common.yes"):this.host._t("common.no")}
    -
    ${this.host._t("vac.diag_map")}${l?.mapId??"default"}
    -
    - ${"missing"===a.status||"disabled"===a.status||"unverified"===a.status?p`
    - ${this.host._t(`vac.source_banner_${a.status}`)} - -
    `:d} - ${$?p`
    - ${this.host._t("vac.xcme_hint")} -
    attributes:
    -  - vacuum_position
    -  - rooms
    -  - path
    -  - map_name
    -
    `:d} -
    - ${this.host._t("vac.choose_source")} -
    - - ${f.map(y)} -
    {t.currentTarget.open?this.host._vacOpenAllCameras(e):(this.host._vacAllCamerasFor=null,this.host._vacAllCameraCache=null)}}> - ${this.host._t("vac.all_cameras")} -
    ${this.host._t("vac.all_cameras_warn")}
    - ${o?b.length?b.map(y):p`
    ${this.host._t("vac.all_cameras_empty")}
    `:d} -
    -
    -
    -
    - ${_?p``:d} - ${h?p``:d} - ${this.host._t("vac.documentation")} -
    - ${l?p` - - - - ${Mo(this,e,a)} - `:d} -
    `}_vacMapId(t,e,s=this.host._planHass){return this.host._vacMapId(t,e,s)}_vacSaveMatrix(t,e,s,i,o=""){return Pl(this,t,e,s,i,o)}_vacPlanRoomAnchors(t){return(this.host._spaceModelById(t)?.rooms||[]).map(t=>({room:t,poly:V(t)})).filter(({room:t,poly:e})=>t.name&&e).map(({room:t,poly:e})=>{const s=oi(e);return s?{name:String(t.name),cx:s[0],cy:s[1]}:null}).filter(Boolean)}async _vacAutoCalibrate(t){if(this.host._markerDialog?.busy)return;const e=this.host._vacSource(t),s=e?ei(this.host.hass?.states[e]?.attributes):null;if(!e||!s||s.rooms.length<3)return void this.host._showToast(this.host._t("vac.autocal_no_rooms"));const i=this._vacMapId(t,s),a=function(t,e,s,i,a){const n=o(t,e,s,i).find(t=>t.source===i&&t.map_id===a);return{space:n?.space||s,routeId:n?.id||""}}(t.id,t.marker?.vacuum??null,t.space,e,i),n=ai(s.rooms,this._vacPlanRoomAnchors(a.space));if(!n)return void this.host._showToast(this.host._t("vac.autocal_no_match"));const r=this.host._serverCfg?.spaces?.find(t=>t.id===a.space),l=Number(r?.cell_cm),h=Number.isFinite(l)&&l>0?l:5,c=ni(n.residual,this.host._gridPitch,h);c>ri?this.host._vacCalConfirm={markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length,error:Se(c,"mi"===this.host.hass?.config?.unit_system?.length)}:await async function(t,e){if(Dl.has(t))return;Dl.add(t);const s=t.host,i=s._markerDialog,o=i?{...i,busy:!0}:null;o&&(s._markerDialog=o,s.requestUpdate());try{await Pl(t,e.markerId,e.source,e.mapId,e.matrix,e.routeId)&&s._showToast(s._t("vac.autocal_done",{rooms:String(e.rooms)}))}finally{Dl.delete(t),o&&s._markerDialog===o&&(s._markerDialog={...o,busy:!1},s.requestUpdate())}}(this,{markerId:t.id,source:e,mapId:i,routeId:a.routeId,space:a.space,matrix:n.matrix,rooms:n.matched.length})}_vacApplyCalibrationProposal(t){return async function(t,e){const s=t.host,i=s._vacCalConfirm;if(!i||i.busy)return;if(e){const t=s._devices.find(t=>t.id===i.markerId),e=n(i.matrix);if(!t||!e)return;const o=i.space||t.space;if(o!==s._space&&!s._commitSpace(o))return;return s._vacCalConfirm=null,s._markerDialog=null,void(s._vacFit={markerId:i.markerId,source:i.source,routeId:i.routeId,mapId:i.mapId,p:e,drag:null})}const o={...i,busy:!0};s._vacCalConfirm=o,s.requestUpdate();const a=await Pl(t,i.markerId,i.source,i.mapId,i.matrix,i.routeId);s._vacCalConfirm===o&&(s._vacCalConfirm=a?null:{...i,busy:!1},s.requestUpdate()),a&&s._showToast(s._t("vac.autocal_done",{rooms:String(i.rooms)}))}(this,t)}_vacStartFit(t,e=""){if(this.host._markerDialog?.busy)return;const s=this.host._vacSource(t),i=s?ei(this.host.hass?.states[s]?.attributes):null;if(!s||!i)return void this.host._showToast(this.host._t("vac.cal_need_pos"));const l=this._vacMapId(t,i),h=function(t,e,s){const i=o(t,e,s.dockSpace,s.source),l=i.find(t=>t.id===s.routeId)||i.find(t=>t.source===s.source&&t.map_id===s.mapId)||null,h=l?.space||s.dockSpace,c=s.viewBoxOf(h);if(!c)return null;const d=a(l?l.calibration:e?.calibration?.[s.mapId]),p=d&&n(d)||r(s.rooms,c);return{space:h,routeId:l?.id||"",params:p}}(t.id,t.marker?.vacuum??null,{routeId:e,source:s,mapId:l,dockSpace:t.space,rooms:i.rooms,viewBoxOf:t=>this.host._spaceModelById(t)?.vb??null});h&&(this.host._markerDialog=null,(h.space===this.host._space||this.host._commitSpace(h.space))&&(this.host._vacFit={markerId:t.id,source:s,mapId:l,routeId:h.routeId,p:h.params,drag:null}))}_vacFitSave(){return async function(t,e){const s=t.host,i=s._vacFit;if(!i||i.busy)return;const o={...i,busy:!0,drag:null};s._vacFit=o,s.requestUpdate();const a=await Pl(t,i.markerId,i.source,i.mapId,e(i.p),i.routeId);s._vacFit===o&&(s._vacFit=a?null:{...i,busy:!1,drag:null},s.requestUpdate()),a&&s._showToast(s._t("vac.cal_done"))}(this,hi)}_vacFitTurn(t){const e=this.host._vacFit;if(!e||e.busy)return;const s=ei(this.host.hass?.states[e.source]?.attributes),i=this._vacGhostCentre(s?.rooms||[]),o={...e.p,...t};this.host._vacFit={...e,p:li(o,e.p,i[0],i[1])}}_vacGhostCentre(t){const e=[],s=[];for(const i of t)e.push(i.x0??i.cx,i.x1??i.cx),s.push(i.y0??i.cy,i.y1??i.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...s)+Math.max(...s))/2]:[0,0]}_vacDelta(t,e,s){const i=this.host._stageEl,o=i?.clientWidth||1,a=i?.clientHeight||1;return[e/o*t.w,s/a*t.h]}_vacFitPointer(t,e){const s=this.host._vacFit;if(!s)return;if(t.stopPropagation(),s.busy)return;if("pointerdown"===t.type){const e=t.target,i=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this.host._vacFit={...s,drag:i?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:Number(i.split(",")[0]),fy:Number(i.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...s.p},fx:0,fy:0}})}const i=s.drag;if(i){if("pointermove"===t.type){const[o,a]=this._vacDelta(e,t.clientX-i.sx,t.clientY-i.sy);if("move"===i.kind)this.host._vacFit={...s,p:{...i.p0,ox:i.p0.ox+o,oy:i.p0.oy+a}};else{const t=ei(this.host.hass?.states[s.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),n=hi(i.p0),[r,l]=ci(n,e[0],e[1]),[h,c]=ci(n,i.fx,i.fy),d=Math.hypot(r-h,l-c)||1,[p,u]=[2*r-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*a-c)/2,m=Math.max(.05,_/d),g={...i.p0,s:i.p0.s*m};this.host._vacFit={...s,p:li(g,i.p0,i.fx,i.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this.host._vacFit={...s,drag:null})}}_renderVacFit(t){const e=this.host._vacFit;if(!e)return d;const s=ei(this.host._renderPlanHass?.states?.[e.source]?.attributes);if(!s)return d;const i=hi(e.p),o=[],a=[],n=[];for(const t of s.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>ci(i,t,e));e.forEach(([t,e])=>{a.push(t),n.push(e)});const[s,r]=ci(i,t.cx,t.cy);o.push(Rt` - ${t.name}`)}let r=d;if(s.pos){const[e,o]=ci(i,s.pos.x,s.pos.y);r=Rt``}const l=[];if(a.length){const e=(()=>{const t=i[0]*i[4]-i[1]*i[3];return(e,s)=>[(i[4]*(e-i[2])-i[1]*(s-i[5]))/t,(-i[3]*(e-i[2])+i[0]*(s-i[5]))/t]})(),s=Math.min(...a),o=Math.max(...a),r=Math.min(...n),h=Math.max(...n),c=.022*t.w,d=c/4;for(const[t,i,a,n]of[[s,r,o,h],[o,r,s,h],[o,h,s,r],[s,h,o,r]]){const s=e(a,n);l.push(Rt` - `)}}return p`this._vacFitPointer(e,t)} - @pointermove=${e=>this._vacFitPointer(e,t)} - @pointerup=${e=>this._vacFitPointer(e,t)} - @pointercancel=${e=>this._vacFitPointer(e,t)}>${o}${r}${l}`}_resetRoomDialogFields(){this.host._roomEditId=null,this.host._roomFill="",this.host._roomCustomFill=null,this.host._roomTempSrc="",this.host._roomHumSrc="",this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomNameScale=1,this.host._roomLabelScale=1}_openRoomEdit(t){if(!t.id)return;this.host._roomEditId=t.id,this.host._nameSel=t.name||"",this.host._areaSel=t.area||"",this.host._roomFill="glow"===t.settings?.fill_mode?"":t.settings?.fill_mode||"";const e=t.settings?.custom_fill;this.host._roomCustomFill=e&&"object"==typeof e?Ns(e,Fs(this.host._curSpaceCfg).customFill):null,this.host._roomTempSrc=t.settings?.temp_source||"",this.host._roomHumSrc=t.settings?.hum_source||"",this.host._roomNameScale=di(t.settings?.name_scale),this.host._roomLabelScale=di(t.settings?.label_scale),this.host._roomSrcOpen=null,this.host._roomSrcFilter="",this.host._roomDialog=!0}_roomSettingsFromDialog(){const t={};return this.host._roomFill&&(t.fill_mode=this.host._roomFill),this.host._roomCustomFill&&(t.custom_fill=this.host._roomCustomFill),this.host._roomTempSrc&&(t.temp_source=this.host._roomTempSrc),this.host._roomHumSrc&&(t.hum_source=this.host._roomHumSrc),1!==this.host._roomNameScale&&(t.name_scale=this.host._roomNameScale),1!==this.host._roomLabelScale&&(t.label_scale=this.host._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this.host._curSpaceCfg,e=t?.rooms.find(t=>t.id===this.host._roomEditId);if(!e)return this.host._roomDialog=!1,void(this.host._roomEditId=null);e.name=this.host._nameSel.trim()||e.name,e.area=this.host._areaSel||null;const s=e.settings||{},i={...s};this.host._roomFill?i.fill_mode=this.host._roomFill:delete i.fill_mode,this.host._roomCustomFill?i.custom_fill=this.host._roomCustomFill:delete i.custom_fill,"glow"===s.fill_mode&&"boolean"!=typeof s.glow&&(i.glow=!0),this.host._roomTempSrc?i.temp_source=this.host._roomTempSrc:delete i.temp_source,this.host._roomHumSrc?i.hum_source=this.host._roomHumSrc:delete i.hum_source,1!==this.host._roomNameScale?i.name_scale=this.host._roomNameScale:delete i.name_scale,1!==this.host._roomLabelScale?i.label_scale=this.host._roomLabelScale:delete i.label_scale,Object.keys(i).length?e.settings=i:delete e.settings,this._saveConfig(),this.host._roomDialog=!1,this.host._roomEditId=null,this.host._nameSel="",this.host._areaSel="",this.host._regSignature="",this.host._maybeRebuildDevices(),this.host.requestUpdate(),this.host._showToast(this.host._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.host._planHass,e=O(this.host._markers),s=this.host._roomSrcFilter.trim().toLowerCase(),i=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();s&&!t.toLowerCase().includes(s)||i.push({value:"device:"+o.id,label:t,sub:o.model||this.host._t("marker.sub_device")})}for(const[o,a]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||a.hidden)continue;if(B(t,o,e))continue;const n=a.name||t.states[o]?.attributes?.friendly_name||o;s&&!(n+" "+o).toLowerCase().includes(s)||i.push({value:"entity:"+o,label:n,sub:o})}return i.sort((t,e)=>t.label.localeCompare(e.label)),i.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),s=t.slice(0,e),i=t.slice(e+1);return"device"===s?this.host._fullRegistryHass.devices[i]?.name_by_user||this.host._fullRegistryHass.devices[i]?.name||i:this.host._fullRegistryHass.entities[i]?.name||this.host.hass.states[i]?.attributes?.friendly_name||i}_labelPos(t,e){const s=this.host._layout["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*lh,y:s.y*lh};const i=this._snap(this.host._roomCenter(t));return{x:i[0],y:i[1]}}_labelDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=this._labelPos(e,s);this.host._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},ch(t),this.host._tip=null}_labelMove(t,e,s){const i="rl_"+(e.id||"");if(!this.host._drag||this.host._drag.id!==i)return;const o=this.host._stageEl;if(!o)return;const a=this.host._spaceModelById(s);if(!a)return;const n=a.vb,r=o.getBoundingClientRect(),l=this.host._viewOr(n),h=(t.clientX-this.host._drag.sx)/r.width*l.w,c=(t.clientY-this.host._drag.sy)/r.height*l.h;Math.abs(t.clientX-this.host._drag.sx)+Math.abs(t.clientY-this.host._drag.sy)>3&&(this.host._drag.moved=!0);const d=ie(this.host._drag.ox+h),p=ie(this.host._drag.oy+c);this.host._savePos({id:i,space:s},d,p)}_labelUp(t){const e="rl_"+(t.id||"");if(!this.host._drag||this.host._drag.id!==e)return;const s=this.host._drag.moved;this.host._drag=s?this.host._drag:null,s&&window.setTimeout(()=>this.host._drag=null,0)}_labelScale(t){const e=this.host._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,s){if("plan"!==this.host._mode)return;t.preventDefault(),t.stopPropagation();const i=t.target.closest(".roomlabel");if(!i)return;const o=i.getBoundingClientRect(),a=o.left+o.width/2,n=o.top+o.height/2,r=Math.max(8,Math.hypot(t.clientX-a,t.clientY-n));this.host._rlResize={id:"rl_"+(e.id||""),space:s,k0:this._labelScale(e),cx:a,cy:n,d0:r},ch(t)}_rlResizeMove(t){const e=this.host._rlResize;if(!e)return;t.stopPropagation();const s=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),i=Math.min(3,Math.max(.5,e.k0*(s/e.d0))),o=this.host._layout[e.id];if(o)this.host._layout={...this.host._layout,[e.id]:{...o,k:i}};else{const t=e.id.slice(3),s=this.host._spaceModelById(e.space);if(!s)return;const o=s.rooms.find(e=>e.id===t);if(!o)return;const a=this._labelPos(o,e.space);this.host._layout={...this.host._layout,[e.id]:{s:e.space,x:a.x/lh,y:a.y/lh,k:i}}}this.host._dirtyPos.add(e.id)}_rlResizeUp(){this.host._rlResize&&(this.host._rlResize=null,this.host._persistLayout())}_renderRoomGear(t,e,s){if(!t.id)return d;let i=null;if(t.poly?(i=this.host._gearPtCache.get(t.poly)||null,i||(i=Te(t.poly),this.host._gearPtCache.set(t.poly,i))):null!=t.x&&null!=t.y&&(i=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!i)return d;const o=(i[0]-s.x)/s.w*100,a=(i[1]-s.y)/s.h*100;return p``}_alignCandidates(){const t=[],e=this.host._spaceModel();if(this.host._markup){if(!e)return t;if(this.host._drag?.id.startsWith("rl_")){const s=this.host._drag.id.slice(3);for(const i of e.rooms){if(!i.name||i.id===s)continue;const e=this._labelPos(i,this.host._space);t.push([e.x,e.y])}return t}for(const s of e.rooms){const e=V(s);if(e)for(const s of e)t.push(s)}if("draw"===this.host._tool)for(const e of this.host._path)t.push(e);if("split"===this.host._tool&&this.host._splitSel?.pts)for(const e of this.host._splitSel.pts)t.push(e);return t}if("devices"===this.host._mode){const e=this.host._deviceDrag?.id??this.host._drag?.id;for(const s of this.host._devices){if(s.space!==this.host._space||s.id===e||"ha_disabled"===s.bindingStatus?.kind)continue;const i=this.host._pos(s);t.push([i.x,i.y])}return t}if("decor"===this.host._mode){const e=this.host._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this.host._decorDraft&&t.push(this.host._decorDraft.a),t}return t}_renderAlignGuides(){const t=this.host._alignPoint;if(!t)return Rt``;const e=this.host._drag?.id.startsWith("rl_")?.5*this.host._gridPitch:.05*this.host._gridPitch,s=pi(t,this._alignCandidates(),e);if(!s.length)return Rt``;const i=this.host._gridPitch,o=1.5*i;return Rt` - ${s.map(e=>{const[s,a,n,r]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return Rt` - `})} - `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180,s=is(15,this.host._cellCm);return Rt``}_renderOpeningDimensionGuides(t){const e=t.labels.flatMap(t=>t.dimension?[t.dimension]:[]);if(!e.length)return Rt``;const s=this._cssPxToRender(4);return Rt``}_renderOpeningPlacementPreview(){const t=this.host._openingPreview;if(!t)return Rt``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:ui(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this.host._cellCm,gridPitch:this.host._gridPitch,face:t.face},s="passage"===t.type?_i(t,is(this.host._gridPitch,this.host._cellCm)):null;return Rt` - `}_renderOpeningDialog(){const t=this.host._openingDialog,e=this.host._spaceModel()?.partitions||[],s=t.id?this.host._curSpaceCfg?.openings?.find(e=>e.id===t.id):null,i={id:t.id||"preview",type:t.type,x:t.x/lh,y:t.y/this.host._spaceH,angle:t.angle,length:s&&!t.lengthTouched?s.length:this.host._cmToUnits(Math.max(20,t.lengthCm))/lh,...t.host?{host:t.host}:{}},o=us(s,i),a=t.host?o?it(i,e,lh,this.host._cellCm,this.host._gridPitch):J(i,e,lh,this.host._cellCm,this.host._gridPitch):null,n="does-not-fit-jamb"===a?.reason,r=t.host?e.find(e=>e.id===t.host.id):null,l=r?Se(r.cm/2,this.host._imperial):"",h=!!t.host&&!a?.resolved&&!n,c="gate"===t.type?"mdi:gate":"window"===t.type?"mdi:window-closed-variant":"passage"===t.type?"mdi:arch":"mdi:door",u=(e,s)=>{const i="contact"===e?t.contact:t.lock,o="contact"===e?!!t.contactOpen:!!t.lockOpen,a="contact"===e?t.contactFilter||"":t.lockFilter||"",n=s.find(t=>t.value===i),r=n?.label||this.host.hass.states[i]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[i]?.name||i,l=Ji(s,a);return p` - - ${o?p`
    - this._filterOpeningEntities(e,t.target.value)} /> -
    - - ${l.map(t=>p` - `)} - ${l.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} -
    -
    `:d}`};return p`this.host._openingDialog=null}> -
    - ${t.host?p` -
    - ${h||n?p``:d} - ${h?this.host._t("opening.partition_orphan"):n?this.host._t("opening.partition_jamb_margin",{distance:l}):this.host._t("opening.host_partition")} -
    `:d} - - - - - - - - {const s=nh(e.target.value);null!=s&&(this.host._openingDialog={...t,lengthCm:s,lengthTouched:!0})}} /> - - ${"passage"===t.type&&(t.contact||t.lock)?p`
    - - ${this.host._t("opening.passage_binding_warning")} -
    `:d} - - ${"passage"!==t.type?p` - ${u("contact",this._contactCandidates())} - ${t.contact?p``:d}`:d} - - ${"door"===t.type||"gate"===t.type?p` - ${u("lock",this._lockCandidates())}`:d} - - ${"gate"!==t.type&&"passage"!==t.type?p``:d} - ${"passage"!==t.type?p``:d} -
    - -
    `}_gridLevels(){const t=this.host._stageEl,e=this.host._viewOr(this.host._baseVb()),s=t&&t.clientWidth&&e.w?t.clientWidth/e.w:1;return fi(this.host._gridPitch,s)}_renderMarkupDefs(t){const e=this._gridLevels();if(!e)return Rt``;const s=this.host._gridPitch*e.fine,i=this.host._gridPitch*e.coarse,o=this.host._gridPitch*e.fine*.14;return Rt` - - - - - - - - - - - - - - `}_renderPhysicalEditorLayer(){const t=this.host._spaceModel();if(!t)return Rt``;const e=this.host._gridPitch,s=this.host._viewOr(this.host._baseVb()),i=this.host._stageEl,o=i?.clientWidth?s.w/i.clientWidth:e/8,a=Math.max(.22*e,8*o),n=Math.max(a,12*o),r=t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`,l=(t,e)=>this.host._physicalSel?.kind===t&&this.host._physicalSel.id===e||"column"===t&&this.host._duplicateColumnId===e,h=(t.room_drafts||[]).flatMap(t=>t.points.slice(0,-1).map((e,s)=>{const i=t.points[s+1],a=Number(t.segments[s]?.cm),n=nt(Number.isFinite(a)?a:15,this.host._cellCm,this.host._gridPitch);return Rt`{this.host._physicalSel={kind:"draft",id:t.id,segment:s},this._registerPhysicalTap("draft",t.id,s)}}>`})),c=(t.partitions||[]).map(t=>{const e=$e(t.a,t.b,t.cm,this.host._cellCm,this.host._gridPitch);return e?Rt`this._physicalDown(e,"partition",t.id)} - @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`:Rt`this._physicalDown(e,"partition",t.id)} - @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`}),p=(t.wall_columns||[]).map(t=>{const e=this.host._physicalRotate?.id===t.id?{...t,angle:this.host._physicalRotate.angle}:t,s=ye(e,this.host._cellCm,this.host._gridPitch);return Rt`this._physicalDown(e,"column",t.id)} - @pointermove=${t=>this._physicalMove(t)} - @pointerup=${t=>this._physicalUp(t)}>`}),u=this.host._physicalDrag,_=(()=>{if(!u?.moved)return d;if("partition"===u.kind&&0===Number(u.base.cm)){const t=u.base;return Rt``}const t="partition"===u.kind?$e(u.base.a,u.base.b,u.base.cm,this.host._cellCm,this.host._gridPitch):ye(u.base,this.host._cellCm,this.host._gridPitch);return t?Rt``:d})(),m=(()=>{const s=this.host._physicalSel;if(!s)return d;if("draft"===s.kind){const e=t.room_drafts.find(t=>t.id===s.id);return e?Rt` - t.join(",")).join(" ")}> - ${e.points.map(t=>Rt``)} - `:d}if("partition"===s.kind){const e=t.partitions.find(t=>t.id===s.id);if(!e)return d;const i=$e(e.a,e.b,e.cm,this.host._cellCm,this.host._gridPitch),o=(e.a[0]+e.b[0])/2,n=(e.a[1]+e.b[1])/2;return Rt` - ${i?Rt``:Rt``} - - - - `}const i=t.wall_columns.find(t=>t.id===s.id);if(!i)return d;const l=this.host._physicalRotate?.id===i.id?{...i,angle:this.host._physicalRotate.angle}:i,h=ye(l,this.host._cellCm,this.host._gridPitch);if("square"!==l.shape)return Rt` - - - `;const c=nt(l.cm,this.host._cellCm,this.host._gridPitch),p=re(l.angle)*Math.PI/180,u=Math.sin(p),_=-Math.cos(p),m=[l.center[0]+u*c/2,l.center[1]+_*c/2],g=[m[0]+u*Math.max(e,24*o),m[1]+_*Math.max(e,24*o)];return Rt` - - - - this._physicalRotateDown(t,i)} - @pointermove=${t=>this._physicalRotateMove(t)} - @pointerup=${t=>this._physicalRotateUp(t)}> - `})();return Rt`${h}${c}${p}${_}${m}`}_renderHiddenWallDiagnosticOverlay(){if(!this.host._markup)return Rt``;const t=this._hiddenWallDiagnosticSnapshot().value;if(!t.segments.length)return Rt``;const e=nt(5,this.host._cellCm,this.host._gridPitch);return Rt``}_renderPlanSnapOverlay(){if(!this.host._markup)return Rt``;const t=this._planSnapGeometrySnapshot().value,e=this.host._activePlanSnapCandidate,s=new Set(this.host._activePlanSnapConflicts.map(t=>t.key)),i=nt(5,this.host._cellCm,this.host._gridPitch),o=nt(10,this.host._cellCm,this.host._gridPitch);return Rt``}_syncPlanSnapActiveMarker(t){const e=this.host.renderRoot?.querySelector('[data-hp="plan-snap-active-marker"]');if(e){if(e.setAttribute("class",`plan-snap-node${t?" active":""}${"line"===t?.kind?" dynamic":""}`),e.setAttribute("data-active",t?"true":"false"),e.setAttribute("visibility",t?"visible":"hidden"),!t)return e.removeAttribute("data-kind"),void e.removeAttribute("data-key");e.setAttribute("data-kind",t.kind),e.setAttribute("data-key",t.key),e.setAttribute("cx",String(t.point[0])),e.setAttribute("cy",String(t.point[1])),e.setAttribute("r",String(nt(10,this.host._cellCm,this.host._gridPitch)))}}_syncPlanSnapConflictMarkers(t){const e=new Set(t.map(t=>t.key));for(const t of this.host.renderRoot?.querySelectorAll('.plan-snap-node[data-kind="endpoint"]')||[])t.setAttribute("class","plan-snap-node"+(e.has(t.dataset.key||"")?" conflict":""))}_planSnapPhysicalSegment(t){let e=0;const s=this.host._spaceModel();if(!s)return null;if("partition"===t.sourceKind)e=Number(s.partitions.find(e=>e.id===t.sourceId)?.cm)||0;else if("draft"===t.sourceKind){const i=/^(.*):(\d+)$/.exec(t.sourceId),o=i?s.room_drafts.find(t=>t.id===i[1]):null;e=o&&i&&Number(o.segments[Number(i[2])]?.cm)||0}else e=bi(s.rooms,this.host._spaceWalls,this.host._openCuts(),[t.a[0],t.a[1],t.b[0],t.b[1]],this.host._wallKeyPitch,this.host._cellCm,this.host._gridPitch,lh);return e>0?{a:[...t.a],b:[...t.b],halfDepth:nt(e,this.host._cellCm,this.host._gridPitch)/2}:null}_drawPreviewJoinPatchD(t,e){if(t.length<2)return"";const s=[];for(let i=0;i+10&&s.push({a:t[i],b:t[i+1],halfDepth:e[i]});if(!s.length)return"";const i=2e-4*this.host._gridPitch,o=this._planSnapGeometrySnapshot().value.segments.filter(e=>t.some(t=>G(t,[e.a[0],e.a[1],e.b[0],e.b[1]])<=i)).map(t=>this._planSnapPhysicalSegment(t)).filter(t=>!!t);return vi([...s,...o],i).map(t=>`M ${t.map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`).join(" ")}_renderMarkupLayer(t){const e=this.host._openCuts(),s=this.host._thickWallCuts(),i=e.concat(s),o=i.length?j(this.host._segments,i,.02*this.host._gridPitch):this.host._segments,a=this.host._path,n=this.host._gridPitch,r=this.host._viewOr(this.host._baseVb()),l="draw"===this.host._tool?this.host._drawWallCm:null,h=(()=>"draw"===this.host._tool&&a.length&&null!=l?this.host._contourClosed?a:this.host._cursorPt?[...a,this.host._cursorPt]:a.length>=2?a:null:null)(),c=h?xn(h.length-1,this.host._contourClosed?[...this.host._draftSegmentCms,this.host._closingWallCm??void 0]:this.host._draftSegmentCms,l,X).map(t=>nt(t,this.host._cellCm,this.host._gridPitch)/2):[],p=h?yi(h,nt(l,this.host._cellCm,this.host._gridPitch)/2,this.host._contourClosed,c):"",u=h?this._drawPreviewJoinPatchD(h,c):"",_=this.host._wallFaceBatch?.candidates[this.host._wallFaceBatch.index]?.repair||this.host._wallRepairDiagnostic;return Rt` - ${this._gridLevels()?Rt``:d} - ${o.map(t=>Rt``)} - ${this._renderPhysicalEditorLayer()} - ${"column"===this.host._tool&&this.host._cursorPt&&this.host._drawWallCm?Rt`{const t={shape:"square",center:this.host._cursorPt,cm:this.host._drawWallCm};return`M ${ye(t,this.host._cellCm,this.host._gridPitch).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`})()}>`:d} - ${p?Rt` - `:d} - ${h&&0===l?Rt`t.join(",")).join(" ")}>`:d} - ${u?Rt` - `:d} - ${_?Rt``:d} - ${"split"===this.host._tool&&this.host._splitSel?.pts?.length?Rt`${this.host._splitSel.pts.length>1?Rt``:d} - ${this.host._splitSel.pts.map((t,e)=>Rt``)} - ${this.host._cursorPt?Rt``:d}`:d} - `}_renderActiveChainInk(){const t=this.host._path,e=this.host._gridPitch;return Rt` - ${t.length>1?Rt``:d} - ${t.length&&this.host._cursorPt&&"draw"===this.host._tool&&!this.host._contourClosed?Rt` - ${this.host._activePlanSnapCandidate||this.host._activePlanSnapConflicts.length?d:Rt``}`:d} - ${t.map((t,s)=>Rt``)} - `}_renderPartitionDeleteDialog(){const t=this.host._partitionDeleteDialog,e="mi"===this.host.hass?.config?.unit_system?.length;return p`this.host._partitionDeleteDialog=null}> -
    -

    ${this.host._t("confirm.delete_partition_openings_body",{count:t.openings.length})}

    -
      - ${t.openings.map(t=>p`
    • ${this.host._t("confirm.delete_partition_openings_item",{type:this.host._t(`opening.${t.type}`),length:Se(t.length*lh/this.host._gridPitch*this.host._cellCm,e)})}
    • `)} -
    -
    -
    - - - -
    -
    `}_renderRoomDeleteDialog(){const t=this.host._roomDeleteDialog;return p`this.host._roomDeleteDialog=null}> -
    -

    ${this.host._t("confirm.delete_room_body")}

    -
    -
    - - - - -
    -
    `}_renderPhysicalDialog(){const t=this.host._physicalDialog,e="column"===t.kind;return p`this.host._physicalDialog=null}> -
    - ${e?p` - `:d} - -
    this.host._physicalDialog={...t,cm:e.target.value}} /> - ${this.host._t(this.host._imperial?"wallthick.unit_in":"wallthick.unit_cm")}
    - ${e&&"square"===t.shape?p` - - this.host._physicalDialog={...t,angle:e.target.value}} />`:d} - ${t.length?p`
    ${this.host._t("physical.length")}: ${t.length}
    `:d} -
    - -
    `}_renderMarkupBar(){const t=this.host._geometryHistory.undoName,e=this.host._geometryHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    -
    - - - - - - - - - - ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} - - - - -
    -
    - -
    -
    `}_renderDevicesBar(){const t=this.host._devicePositionHistory.undoName,e=this.host._devicePositionHistory.redoName,s=t?this.host._t("history.undo_named",{name:t}):this.host._t("history.undo_empty"),i=e?this.host._t("history.redo_named",{name:e}):this.host._t("history.redo_empty");return p`
    -
    - - - - - ${this.host._editorToolbarGroups.map(t=>this._renderEditorGroupLauncher(t))} -
    -
    - - - -
    -
    `}_discoveryFilterState(t){const e=this.host._settings,s=void 0!==t.draftGroupLights?t.draftGroupLights:!1!==e.group_lights,i=e.exclude_integrations,o=void 0!==t.draftExcluded?null===t.draftExcluded?[...$i]:t.draftExcluded:i?[...i]:[...$i],a=void 0!==t.draftExcluded?null===t.draftExcluded:!i,n=void 0!==t.draftGroupLights||void 0!==t.draftExcluded;return{groupLights:s,excluded:o.sort(),usesProductList:a,dirty:n}}_registryIntegrations(){const t=new Set,e=this.host._fullRegistryHass;for(const s of Object.values(e?.entities||{}))s?.platform&&t.add(String(s.platform));for(const s of Object.values(e?.devices||{})){const e=s?.identifiers?.[0],i=Array.isArray(e)?e[0]:null;i&&t.add(String(i))}return[...t].sort()}_discoveryFilterPreview(t){const e=this._discoveryFilterState(t),s=[e.groupLights?1:0,e.usesProductList?"p":e.excluded.join(","),this.host._cfgEpoch,this.host._regSignature].join("|");if(this._discoveryPreviewMemo?.key===s)return this._discoveryPreviewMemo.value;const i={hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),markers:this.host._markers,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||"",loc:t=>this.host._t(t),iconRules:this.host._iconRules},o=(t,e)=>new Set(Yi({...i,settings:t,excluded:e})),a=(t,e)=>new Set(Zi({...i,settings:t,excluded:e}).map(t=>t.bindingRef||t.id)),n=wi(this.host._settings),r={...this.host._settings,group_lights:e.groupLights},l=new Set(e.excluded),h=o(this.host._settings,n),c=o(r,l),d=a(this.host._settings,n),p=a(r,l);let u=0,_=0,m=0;const g=t=>t.includes("light.");for(const t of h)c.has(t)||(u++,g(t)&&m++);for(const t of c)h.has(t)||(_++,g(t)&&m++);for(const t of p)d.has(t)||(u++,g(t)&&m++);for(const t of d)p.has(t)||(_++,g(t)&&m++);const f={appear:u,hide:_,lights:m};return this._discoveryPreviewMemo={key:s,value:f},f}async _saveDiscoveryFilters(t){const e=this.host._serverCfg;if(!e)return;const s=this._discoveryFilterState(t),i={...e.settings};s.groupLights?delete i.group_lights:i.group_lights=!1,s.usesProductList?delete i.exclude_integrations:i.exclude_integrations=s.excluded,this.host._serverCfg={...e,settings:i},this.host._deviceInboxMemo=null,this._discoveryPreviewMemo=null,this.host._regSignature="",this.host._deviceInbox&&(this.host._deviceInbox={...t,draftGroupLights:void 0,draftExcluded:void 0}),this._saveConfig(),this.host._maybeRebuildDevices(),this.host.requestUpdate()}_renderDiscoveryFilters(t){const e=this._discoveryFilterState(t),s=e=>this.host._deviceInbox={...t,...e},i=t.filtersOpen&&e.dirty?this._discoveryFilterPreview(t):null,o=this._registryIntegrations().filter(t=>!e.excluded.includes(t));return p`
    s({filtersOpen:t.target.open})}> - ${this.host._t("device_inbox.filters_title")} - -
    - ${this.host._t("device_inbox.filters_excluded")} -
    - ${e.excluded.map(t=>p`${t} - - `)} -
    - {const i=t.target,o=i.value.trim();o&&!e.excluded.includes(o)&&s({draftExcluded:[...e.excluded,o]}),i.value=""}} /> - - ${o.map(t=>p``)} - - -
    - ${i?p`
    - ${this.host._t("device_inbox.filters_preview_appear",{count:String(i.appear)})} - · ${this.host._t("device_inbox.filters_preview_hide",{count:String(i.hide)})} - · ${this.host._t("device_inbox.filters_preview_lights",{count:String(i.lights)})} -
    `:d} - -
    `}_renderDeviceInbox(){const t=this.host._deviceInbox,e=this._deviceInboxRows(),s=Object.fromEntries(["on_plan","available","hidden","readd"].map(t=>[t,e.filter(e=>e.category===t).length])),i=function(t,e,s,i=!1){const o=s.trim().toLocaleLowerCase();return t.filter(t=>t.category===e&&(!i||t.isNew)&&(!o||t.searchText.includes(o)))}(e,t.tab,t.search,"on_plan"===t.tab&&t.onlyNew),o=i.slice(0,t.limit),a=t=>this.host._t(`device_inbox.tab_${t}`),n=`device_inbox.empty_${t.tab}`;return p`this.host._deviceInbox=null}> -
    -
    - this.host._deviceInbox={...t,search:e.target.value,limit:100}} /> - -
    -
    this._deviceInboxTabKey(t)}> - ${["on_plan","available","hidden","readd"].map(e=>p` - `)} -
    -
    - ${"on_plan"===t.tab?p``:d} - ${"available"===t.tab?p``:d} - - - ${this._help("device_inbox.show_hidden.help")} - -
    - ${"available"===t.tab?this._renderDiscoveryFilters(t):d} -
    - ${o.length?o.map(t=>{const e="on_plan"===t.category?t.canFind?p``:p``:"hidden"===t.category?p``:p``,s="active"===t.status.kind?"":this.host._t(`device_inbox.status_${t.status.kind}`);return p`
    - -
    -
    ${t.name} - ${t.isNew?p`${this.host._t("device_inbox.new")}`:d} -
    -
    - ${[t.model,t.integration,t.spaceName,t.areaName].filter(Boolean).join(" · ")} -
    -
    - ${"excluded_integration"===t.reason&&t.integration?this.host._t("device_inbox.reason_excluded_integration",{integration:t.integration}):this.host._t(`device_inbox.reason_${t.reason}`)} - ${s?p`${s}`:d} -
    - ${t.binding} -
    -
    - ${e} - ${t.canEdit||t.canHide||"available"===t.category||"hidden"===t.category||this.host._bindingHasHaPage(t.binding)?p` -
    - - - -
    - ${t.canEdit&&("on_plan"!==t.category||t.canFind)?p``:d} - ${t.canHide?p``:d} - ${"available"===t.category?p``:d} - ${"hidden"===t.category?p``:d} - ${this.host._bindingHasHaPage(t.binding)?p``:d} -
    -
    `:d} -
    -
    `}):p`
    ${this.host._t(n)}
    `} -
    - ${i.length>o.length?p``:d} -
    -
    - -
    -
    `}_markerValueBadgeFields(t){return ki({touched:t.valueBadgeTouched,originalHas:t.originalHasValueBadge,original:t.originalValueBadge,enabled:t.valueBadgeEnabled,source:t.valueBadgeSource,position:t.valueBadgePosition})}_markerValueSourceFields(t){return xi({touched:t.valueSourceTouched,originalHas:t.originalHasValueSource,original:t.originalValueSource,source:t.valueSource})}_markerDraft(t){if("ha"===t.bindingMode&&(!t.binding||"virtual"===t.binding))return null;const e=Cs(t.room),s=ys(t.binding,t.devId,()=>"__hp_device_preview__"),i=this.host._markers.find(e=>e.id===s||e.id===t.devId),o=xs(t.binding,t.controls,this.host._bindingEntities(t.binding)),a=this._effectiveStoredTapAction(t),n={id:s,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&1.5!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle||null,...this._markerTapActionFields(t),...this._markerToggleEntityFields(t),tap_target:"run"===a&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:o.length?o:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),...this._markerValueSourceFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=nh(t.glowRadius);return null==e||e<=0?null:Math.round(this.host._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:t.hideFromPlan,vacuum:i?.vacuum||null},r=!!i&&("string"==typeof i.area&&i.area.length>0||null===i.area&&!!i.space&&!!i.room_id);return"virtual"===t.binding||t.roomTouched?(n.space=e?.space||("virtual"===t.binding?this.host._space:null),n.area=e?.area||null,n.room_id=e?.roomId||null):r&&(n.space=i.space,n.area=i.area,n.room_id=i.room_id),n}_markerPreviewDevice(t){const e=this._markerDraft(t);if(!e)return null;const s=`${this.host._haRegistry.revision}\n${_(this.host._markers)}\n${JSON.stringify(e)}`;if(this.host._markerPreviewMemo?.key===s)return this.host._markerPreviewMemo.device;const i=Si({hass:this.host.hass,registry:this.host._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this.host._areaToSpace).map(([t,e])=>[t,e.space])),marker:e,siblingMarkers:this.host._markers,settings:this.host._settings,excluded:this.host._excluded,showAll:this.host._showAll,firstSpaceId:this.host._model[0]?.id||this.host._space,loc:t=>this.host._t(t),iconRules:this.host._iconRules});return this.host._markerPreviewMemo={key:s,device:i},i}_markerPreviewDevices(t){const e=this.host._markerPreviewDevicesMemo;if(e?.base===this.host._devices&&e.preview===t)return e.devices;const s=[...this.host._devices.filter(e=>e.id!==t.id),t];return this.host._markerPreviewDevicesMemo={base:this.host._devices,preview:t,devices:s},s}_toggleIntent(t,e=this.host._devices){return this.host._toggleIntent(t,e)}_toggleIntentForDialog(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s=this._markerPreviewDevices(e);return this._toggleIntent(e,s)}_toggleStateText(t,e){return this.host._toggleStateText(t,e)}_toggleConfirmationStateText(t){return this.host._toggleConfirmationStateText(t)}_toggleConfirmationLines(t){return this.host._toggleConfirmationLines(t)}_toggleHintLines(t){if(!t)return[];const e=t=>this.host._t(`marker.toggle_effect_${t.replace("-","_")}`),s=t=>this.host._t(`marker.toggle_skip_${t.replace("-","_")}`);return Mi(t,{single:t=>{if("via"in t&&"virtual-light"===t.via)return this.host._t("marker.virtual_light_target",{name:t.name});const e=t.entityId||("ref"in t?t.ref:""),s=t.name||e;return this.host._t("marker.toggle_hint_single",{name:s,id:e})},group:t=>this.host._t("marker.toggle_hint_group",{count:t.length,names:t.map(t=>`${t.name} (${t.entityId})`).join(", ")}),currentNext:(t,s)=>"virtual-light"===t.via?this.host._t("marker.virtual_light_current",{state:this.host._t("on"===t.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:e(s)}):this.host._t("marker.toggle_hint_current",{state:this._toggleStateText(t.entityId,t.state),effect:e(s)}),groupCurrentNext:(t,s)=>this.host._t("marker.toggle_hint_group_current",{on:t.filter(t=>"on"===t.state).length,count:t.length,effect:e(s)}),skipped:t=>this.host._t("marker.toggle_hint_skipped",{count:t.length,targets:t.map(t=>{const e=t.entityId||t.ref;return`${t.name||e} (${e}: ${s(t.reason)})`}).join(", ")}),none:t=>this.host._t(`marker.toggle_none_${t.replaceAll("-","_")}`)})}_effectiveStoredTapAction(t,e){return t.tapActionTouched?t.tapAction:Ss(t.originalHasTapAction?t.originalTapAction:null,e)}_effectiveMarkerTapAction(t,e=this._markerPreviewDevice(t)){return this._effectiveStoredTapAction(t,e?.primary?.split(".")[0])}_announceToggleDraft(t){const e=this._markerPreviewDevice(t),s=t.tapActionTouched?t:{...t,tapAction:this._effectiveMarkerTapAction(t,e)},i="toggle"===s.tapAction?this._toggleHintLines(this._toggleIntentForDialog(s)).join(" "):"";return{...s,tapHintAnnouncement:i}}_valueBadgeForBinding(t,e){const s={...t,binding:e,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},i=this._markerPreviewDevice(s);if(!i)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const o=[...this.host._devices.filter(t=>t.id!==i.id),i],a=ws(this.host._planHass,i,o),n=ks(this.host._planHass,i,a);return{valueBadgeEnabled:t.valueBadgeEnabled&&!!n,valueBadgeSource:n,valueBadgeTouched:!0}}_markerSpatialSource(t){const e=this._markerPreviewDevice(t);if(!e)return null;const s={...e,hidden:!1};return Di(Ci(this.host._planHass,[...this.host._devices.filter(t=>t.id!==s.id),s],null,this.host._virtualLights).filter(t=>t.device.id===s.id))}_markerAutoHasSpatialSource(t){const e={...t,lightRole:"auto",lightRoleTouched:!0},s=this._markerPreviewDevice(e);return!!s&&Pi(this.host._planHass,{...s})}_setMarkerLightRole(t){const e=this.host._markerDialog;e&&(this.host._markerDialog={...e,lightRole:t,lightRoleTouched:!0})}_controlRefInfo(t){if(!t.startsWith("marker:"))return{label:this.host.hass.states[t]?.attributes?.friendly_name||t,sub:t,icon:t.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this.host._planEntityAvailable(t)};const e=t.slice(7),s=this.host._markers.find(t=>t.id===e),i=this.host._devices.find(t=>t.id===e);if(!s||s.removed||!0!==s.is_light)return{label:this.host._t("marker.control_missing_label"),sub:`${this.host._t("marker.control_broken")} (${t})`,icon:"mdi:alert-outline",warning:!0};const o=this.host._serverCfg?.spaces.find(t=>t.id===(i?.space||s.space)),a=!!i&&Ii(i).length>0;return{label:s.name||i?.name||e,sub:[o?.title||i?.space||s.space,s.room_id||i?.area,a?"":this.host._t("marker.control_passive")].filter(Boolean).join(" · "),icon:s.icon||i?.icon||"mdi:lightbulb-outline",warning:!0===s.hidden||!0===i?.hidden}}_valueBadgeCandidateLabel(t){const e=t.source;if("derived_lqi"===e.kind)return this.host._t("marker.value_badge_lqi");if("derived_marker_state"===e.kind)return this.host._t("marker.value_badge_marker_state",{name:t.label});if("entity_state"===e.kind)return this.host._t("marker.value_badge_state",{name:t.label});const s=this.host.hass.states[e.entity_id]?.attributes?.friendly_name||this.host._fullRegistryHass.entities[e.entity_id]?.name||e.entity_id;return this.host._t(`marker.value_badge_attr_${e.attribute}`,{name:s})}_controlCandidates(t){const e=this._markerDraft(t)?.id||t.devId||"",s=[],i=new Set;for(const t of this.host._markers){if(t.id===e||t.removed||t.hidden||!0!==t.is_light)continue;if("virtual"!==t.binding&&"active"!==this.host._bindingStatus(t.binding).kind)continue;const o=this.host._devices.find(e=>e.id===t.id);if(!o||o.hidden)continue;const a=this._controlRefInfo(`marker:${t.id}`);for(const t of Ii(o))i.add(t);s.push({value:`marker:${t.id}`,label:a.label,sub:a.sub,icon:a.icon,search:`${a.label} ${a.sub} ${t.id} ${Ii(o).join(" ")}`.toLowerCase()})}const o=Object.keys(this.host.hass.states||{}).filter(e=>Ti(e)&&!i.has(e)&&Ri(t.binding,[e],this.host._bindingEntities(t.binding)).length>0&&this.host._planEntityAvailable(e)).map(t=>{const e=this._controlRefInfo(t);return{value:t,label:e.label,sub:t,icon:e.icon,search:`${e.label} ${t}`.toLowerCase()}}),a=t.controlsFilter.trim().toLowerCase();return[...s,...o].filter(e=>!t.controls.includes(e.value)&&(!a||e.search.includes(a))).slice(0,12).map(({search:t,...e})=>e)}_addControlRef(t,e){if(e.startsWith("marker:")){const s=this._markerDraft(t)?.id||t.devId||"";if(!s)return void this.host._showToast(this.host._t("toast.marker_binding_required"));const i=e.slice(7),o=this._markerDraft(t),a=o?[...this.host._markers.filter(t=>t.id!==s),o]:this.host._markers;if(zi(a,s,i))return void this.host._showToast(this.host._t("toast.marker_control_cycle"))}this.host._markerDialog=this._announceToggleDraft({...t,controls:[...t.controls,e],controlsFilter:""})}_setMarkerGlowMode(t){const e=this.host._markerDialog;if(!e)return;if("auto"===t)return void(this.host._markerDialog={...e,glowMode:t,glowTouched:!0});const s=this._markerSpatialSource(e),i=Ai(s?this.host._planHass.states[s.eid]:void 0,null,this.host._fillColors.glow_light.c),o=!e.glowColorDrafted,a="fixed"===t&&!e.glowBrightnessDrafted;this.host._markerDialog={...e,glowMode:t,glowColor:o?i.c:e.glowColor,glowBrightness:a?Math.max(1,Math.round(100*i.bri)):e.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:e.glowBrightnessDrafted||"fixed"===t,glowTouched:!0}}_renderMarkerDialog(){const t=this.host._markerDialog,e="virtual"===t.bindingMode,s=this._bindingCandidates();this.host._bindingEntities(t.binding);const i=e?null:this.host._bindingStatus(t.binding),o=!e&&this.host._bindingHasHaPage(t.binding),a=this._markerPreviewDevice(t),n=this._effectiveMarkerTapAction(t,a),r=a?Fs(this.host._serverCfg?.spaces.find(t=>t.id===a.space)):null,l=a?this._markerPreviewDevices(a):this.host._devices,h="toggle"===n&&a?this._toggleIntent(a,l):null,c=this._toggleHintLines(h),u=a?Fi(this.host._planHass,a,{liveStates:!1!==this.host._config?.live_states,showTemperature:!1!==this.host._config?.show_temperature,showSignal:r?.showLqi??!1!==this.host._config?.show_signal,designPreview:!0,activityRuntime:this.host._activityRt.get(a.id),lightDevices:l,registryHass:this.host._fullRegistryHass,reducedMotion:this.host._reducedMotion}):null,_=a?ws(this.host._planHass,a,l):[],m=a?ks(this.host._planHass,a,_):null,g=t.valueBadgeTouched?t.valueBadgeEnabled:!!u?.valueBadge,f=t.valueBadgeTouched?t.valueBadgeSource:u?.valueBadge?.source||t.valueBadgeSource,b=t.valueBadgeTouched?t.valueBadgePosition:u?.valueBadge?.position||t.valueBadgePosition,v=Ni(f),y=!!f&&!_.some(t=>t.key===v),$=_.find(t=>t.key===v),w=Ni(t.valueSource),k=!(!t.valueSource||w&&_.some(t=>t.key===w)),x=_.find(t=>t.key===w),S=u?.valueSource?.sourceKey||"",M=this._markerAutoHasSpatialSource(t),D=!!a&&Hi(this.host._planHass,{...a}),C=Li(t.lightRole,M,D,t.glowMode),P=!C.sourceExists,I=!C.fromSourceEnabled,T=C.passive,R=C.effectiveMode,z="never"===t.lightRole?this.host._t("marker.glow_disabled_never"):"auto"!==t.lightRole||M?T?this.host._t("marker.glow_passive_hint"):this.host._t("marker.glow_disabled_no_entity"):this.host._t("marker.glow_disabled_auto"),A=a?Ii(a):[],F=a&&Ei(a)||"",N=!!t.lightEntity&&!A.includes(t.lightEntity),H=a?Oi(a):[],L=!!t.toggleEntity&&!H.includes(t.toggleEntity),E="toggle"===n?this._toggleIntentForDialog({...t,toggleEntity:"",toggleEntityTouched:!0}):null,O=E?[...E.targets,...E.skippedTargets].map(t=>t.entityId||("ref"in t?t.ref:"")).filter(Boolean).join(", "):"",B=(()=>{if(e)return null;const i=s.find(e=>e.value===t.binding);if(i)return i.label;const[o,a]=t.binding.split(":");return"device"===o?this.host._fullRegistryHass.devices[a]?.name_by_user||this.host._fullRegistryHass.devices[a]?.name||a:this.host._fullRegistryHass.entities[a]?.name||this.host.hass.states[a]?.attributes?.friendly_name||a})();return p`this._closeMarkerDialog()}> -
    - ${"ha_disabled"===i?.kind?p`
    - - ${this.host._t(`marker.ha_disabled_${i.reason}`)} - ${o?p``:d} -
    `:"unverified"===i?.kind&&t.binding?p`
    - - ${this.host._t("marker.ha_registry_limited")} -
    `:d} - - this.host._markerDialog={...t,name:e.target.value}} /> - - -
    - -
    - - -
    - ${"ha"===t.bindingMode?p` - ${t.bindingOpen?p`
    - this.host._markerDialog={...t,bindingFilter:e.target.value}} /> -
    - ${s.map(e=>p`
    {if(e.value===t.binding)return void(this.host._markerDialog={...t,bindingOpen:!1});const s={...t,binding:e.value,bindingOpen:!1,controls:xs(e.value,t.controls,this.host._bindingEntities(e.value)),autoIcon:this.host._autoIconForBinding(e.value)};this.host._markerDialog=this._announceToggleDraft({...s,...this._valueBadgeForBinding(s,e.value),valueSource:null,valueSourceTouched:!0})}}> - ${e.label}${e.sub} -
    `)} - ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} -
    -
    `:d}`:d} -
    - - - - - ${this._renderVacSection(t)} - - - - ${"toggle"===n&&(H.length>1||L)?p`
    -
    - - ${this._help("marker.toggle_entity.help")} -
    - - ${L?p`

    - - ${this.host._t("marker.toggle_entity_missing",{entity:t.toggleEntity,fallback:O||this.host._t("marker.toggle_entity_none")})} -

    `:d} -
    `:d} - ${"toggle"===n?p`
    - ${c.map(t=>p`
    ${t}
    `)} -
    -
    ${t.tapHintAnnouncement}
    `:d} - ${"run"===n?(()=>{const e=t.runFilter.trim().toLowerCase(),s=this._runCandidates().filter(t=>!e||t.label.toLowerCase().includes(e)||t.value.includes(e)),i=t.tapTarget?this._runCandidates().find(e=>e.value===t.tapTarget):null;return p` - - ${t.tapTarget&&!i?p`
    ${this.host._t("marker.run_target_gone",{id:t.tapTarget})}
    `:d} - {t.target.select()}} - @input=${e=>this.host._markerDialog={...t,runFilter:e.target.value,tapTarget:""}} /> - ${i?d:p`
    - ${s.slice(0,40).map(e=>p`
    this.host._markerDialog={...t,tapTarget:e.value,runFilter:""}}> - ${e.label}${e.sub} -
    `)} - ${s.length?d:p`
    ${this.host._t("marker.nothing_found")}
    `} -
    `}`})():d} - ${"run"===n||"toggle"===n?p``:d} - -
    - - ${this._help("marker.controls.help")} -
    - ${t.controls.length?p`
    - ${t.controls.map(e=>{const s=this._controlRefInfo(e);return p` - ${s.label} - this.host._markerDialog=this._announceToggleDraft({...t,controls:t.controls.filter(t=>t!==e)})}> - `})} -
    `:d} - this.host._markerDialog={...t,controlsFilter:e.target.value}} /> - ${t.controlsFilter.trim()?p`
    - ${this._controlCandidates(t).map(e=>p``)} -
    `:d} - - ${this.host._bindingHasClimate(t.binding)?p``:d} -
    - ${this.host._t("marker.light_role_label")}${this._help("marker.light_role.help")} -
    - - - -
    -
    - - ${"always"===t.lightRole&&(A.length>1||N)?p`
    -
    - - ${this._help("marker.light_entity.help")} -
    - - ${N?p`

    - - ${this.host._t("marker.light_entity_missing",{entity:t.lightEntity,fallback:F||"—"})} -

    `:d} -
    `:d} - -
    - ${this.host._t("marker.glow_color_label")}${this._help("marker.glow_mode.help")} -
    - - - -
    - ${"auto"!==R?p`
    - {this.host._markerDialog={...t,glowMode:R,glowColor:e.detail.color,glowColorDrafted:!0,glowTouched:!0}}}> - ${"fixed"===R?p` - ${this.host._t("marker.glow_brightness")} - ${this._rangeInput(1,100,1,t.glowBrightness,e=>{this.host._markerDialog={...t,glowMode:R,glowBrightness:e,glowBrightnessDrafted:!0,glowTouched:!0}},P,this.host._t("marker.glow_brightness"))} - ${Math.round(t.glowBrightness)}%`:d} -
    `:d} -
    -
    -
    - - ${this._help("marker.glow_radius.help")} -
    -
    - this.host._markerDialog={...t,glowRadius:e.target.value}} /> - ${this.host._imperial?this.host._t("gs.unit_ft"):this.host._t("gs.unit_m")} -
    -
    - ${P||T?p`

    - ${z} -

    `:d} - - - ${customElements.get("ha-icon-picker")?p`{const s=e.detail.value||"";(t.icon||s!==t.autoIcon)&&(this.host._markerDialog={...t,icon:s})}}>`:p`this.host._markerDialog={...t,icon:e.target.value}} />`} - ${!t.icon&&t.autoIcon?p`

    - ${this.host._t("marker.icon_auto",{icon:t.autoIcon})} -

    `:d} - - - -

    ${this.host._t(ih[t.display])}

    - ${"value"===t.display?p`
    -
    - - ${this._help("marker.value_source.help")} -
    - - ${x?p`

    ${x.technical}

    `:d} - ${e?p`

    - - ${this.host._t("marker.preview.reason.value_virtual")} -

    `:k?p`

    - ${this.host._t("marker.value_source_missing_hint")} -

    `:d} -
    `:d} - ${"static_icon"===t.display&&this.host._bindingHasAlarm(t.binding)?p`
    - - ${this.host._t("marker.static_alarm_warning")} -
    `:d} -
    - ${this.host._t("marker.value_badge_title")}${this._help("marker.value_badge.help")} - - ${"static_icon"===t.display?p`

    - ${this.host._t("marker.value_badge_static")} -

    `:_.length||t.valueBadgeSource?d:p`

    - ${this.host._t("marker.value_badge_empty")} -

    `} - ${g?p` -
    - - ${this._help("marker.value_badge_source.help")} -
    - - ${$?p`

    ${$.technical}

    `:d} - ${y?p`

    - ${this.host._t("marker.value_badge_missing_hint")} -

    `:d} - ${"value"===t.display&&v===S?p`

    - ${this.host._t("marker.value_badge_duplicate")} -

    `:d} -
    - - ${this._help("marker.value_badge_position.help")} -
    - - `:d} -
    - ${u?p` - `:p`
    - - ${this.host._t("marker.preview.select_source")} -
    `} - ${"icon_ripple"===t.display?p`
    - {this.host._markerDialog={...t,rippleColor:e.detail.color}}}> -
    -
    - ${this.host._t("marker.ripple_size")} - ${this._rangeInput(1,8,.5,t.rippleSize,e=>this.host._markerDialog={...t,rippleSize:e})} - ×${t.rippleSize} -
    -

    ${this.host._t("marker.activity_alarm_note")}

    `:d} - - -
    - ${this._rangeInput(.5,3,.1,t.size,e=>this.host._markerDialog={...t,size:e})} - ×${t.size.toFixed(1)} - ${this.host._t("marker.angle_label")} - ${""} - ${this._rangeInput(0,355,5,t.angle,e=>this.host._markerDialog={...t,angle:e})} - ${t.angle}° -
    - - - this.host._markerDialog={...t,model:e.target.value}} /> - - - this.host._markerDialog={...t,link:e.target.value}} /> - - - - - -
    - ${t.pdfs.map(t=>p` - ${t.name} - this._removeMarkerPdf(t.url)}>`)} - - - this._pickMarkerFiles(t)} /> - -
    -
    -
    -
    - ${t.devId?p``:d} - ${t.devId?p``:d} -
    -
    - - -
    -
    -
    `}_renderSpaceDialog(){const t=this.host._spaceDialog,e=this.host._importTotal>0&&"create"===t.mode?this.host._t("import.progress",{i:this.host._importTotal-this.host._importQueue.length,n:this.host._importTotal}):"",s=()=>{this.host._spaceDialog=null,this.host._importQueue=[],this.host._importTotal=0};return p` -
    - - this.host._spaceDialog={...t,title:e.target.value}} /> - - - ${"file"===t.source?p`
    - ${t.planFile?p`${t.planFile.name}`:t.planUrl?p`${this.host._t("space.plan_alt")}`:p`${this.host._t("space.no_plan")}`} - - - this._pickPlanFile(t)} /> - - -
    - ${t.pickSaved?this._renderServerPlans(t):d}`:d} - - -
    - - ${this._help("space.cell_cm.help")} -
    -
    - {const s=e.target.value,i=nh(s),o=null==i?null:qi(i,this.host._imperial);this.host._spaceDialog={...t,cellCmInput:s,cellCmTouched:!0,cellCm:null!=o&&o>0?Math.max(.1,Math.min(ah,o)):t.cellCm}}} /> - ${this.host._t(this.host._imperial?"space.scale_unit_imperial":"space.scale_unit")} -
    - - - -
    - - ${this._help("space.zero_wall_style.help")} -
    - - - - ${""} - -
    ${this.host._t("space.hide_decor_tip")}
    - -
    ${this.host._t("space.hide_openings_tip")}
    - - ${[["labelTemp","space.label_temp"],["labelHum","space.label_hum"],["labelLqi","space.label_lqi"],["labelLight","space.label_light"]].map(([e,s])=>p``)} - -
    - ${this._rangeInput(50,300,5,Math.round(100*t.cardFontScale),e=>this.host._spaceDialog={...t,cardFontScale:e/100})} - ${Math.round(100*t.cardFontScale)}% -
    - ${this.host._renderCardPreview(t.cardFontScale,1,1)} -
    - {this.host._spaceDialog={...t,roomColor:e.detail.color,roomOpacity:e.detail.opacity}}}> -
    -
    - - ${this._help("space.bg_mode.help")} -
    - - ${"static"===(t.bgMode??Yt(this.host._settings,{}))?p`
    - {this.host._spaceDialog={...t,bgColor:e.detail.color}}}> - ${t.bgColor?p``:p`${this.host._t("space.bg_inherited")}`} -
    `:d} -
    - - ${this._help("space.north.help")} -
    -
    - {const s=e.target.value.trim(),i=""===s?null:Math.round(Number(s));this.host._spaceDialog={...t,northDeg:null!==i&&Number.isFinite(i)?Math.min(359,Math.max(0,i)):null}}} /> - ${null===t.northDeg?this.host._t("space.north_inherited",{v:null===Zt(this.host._settings,{})?"—":String(Zt(this.host._settings,{}))+"°"}):"°"} -
    - - -
    - ${this.host._t("space.fill_label")} - ${this._help("space.fill_mode.help")} -
    - ${Ui.map(t=>[t,"fill."+t]).map(([e,s])=>p` - ${"custom"===e&&"custom"===t.fillMode?p`
    - ${this.host._t("space.custom_fill")} - {this.host._spaceDialog={...t,customFill:{c:e.detail.color,a:e.detail.opacity}}}}> - ${t.customFill?p``:d} -
    `:d}`)} - - ${t.deleteBlockers?p``:d} -
    - -
    `}_renderMergeDialog(){const t=this.host._mergeDialog,e=this.host._spaceModel()?.rooms||[],s=(s,i)=>{const o=e.find(t=>t.id===s),a=o?.area?this.host.hass.areas[o.area]?.name:null;return p``};return p`this.host._mergeDialog=null}> -
    -

    ${this.host._t("merge.hint")}

    - - ${s(t.aId,"a")} - ${s(t.bId,"b")} -
    -
    - - - -
    -
    `}_renderRoomSource(t){const e="temp"===t?this.host._roomTempSrc:this.host._roomHumSrc,s=e=>{"temp"===t?this.host._roomTempSrc=e:this.host._roomHumSrc=e,this.host.requestUpdate()},i=this.host._roomSrcOpen===t;return p` - - - - ${e||i?p` - ${i?p`
    - {this.host._roomSrcFilter=t.target.value,this.host.requestUpdate()}} /> -
    - ${this._roomSrcCandidates().map(t=>p`
    {s(t.value),this.host._roomSrcOpen=null}}> - ${t.label}${t.sub} -
    `)} -
    -
    `:d}`:d}`}_renderRoomDialog(){const t=!!this.host._roomEditId,e=t?null:this.host._wallFaceBatch,s=e&&e.candidates.length>1?this.host._t("room.queue_progress",{current:e.index+1,total:e.candidates.length}):"",i=!!this.host._areaSel||!!this.host._nameSel.trim(),o=Fs(this.host._curSpaceCfg),a=this.host._roomFill||o.fill,n=this.host._roomCustomFill||o.customFill,r=[...this.host._freeAreas];if(t&&this.host._areaSel&&!r.some(t=>t.area_id===this.host._areaSel)){const t=this.host.hass.areas[this.host._areaSel];t&&r.unshift(t)}return p`this._roomDialogCancel()}> -
    - ${s?p`

    - ${s} -

    `:d} - - this.host._nameSel=t.target.value} /> - - - - - - ${[["","fill.inherit"],...Ki.map(t=>[t,"fill."+t])].map(([t,e])=>p``)} - ${"custom"===a?p`
    - ${this.host._roomCustomFill?this.host._t("room.custom_fill_own"):this.host._t("room.custom_fill_space")} - {this.host._roomCustomFill={c:t.detail.color,a:t.detail.opacity}}}> - ${this.host._roomCustomFill?p``:d} -
    `:d} - ${this._renderRoomSource("temp")} - ${this._renderRoomSource("hum")} - - - -
    - ${this._rangeInput(50,300,5,Math.round(100*this.host._roomNameScale),t=>{this.host._roomNameScale=t/100,this.host.requestUpdate()})} - ${Math.round(100*this.host._roomNameScale)}% -
    - -
    - ${this._rangeInput(50,300,5,Math.round(100*this.host._roomLabelScale),t=>{this.host._roomLabelScale=t/100,this.host.requestUpdate()})} - ${Math.round(100*this.host._roomLabelScale)}% -
    - ${this.host._renderCardPreview(Fs(this.host._curSpaceCfg).cardFontScale,this.host._roomNameScale,this.host._roomLabelScale)} -
    -
    - - - ${t?p``:p`${this.host._pendingSplit?d:p``} - `} -
    -
    `}}export{dh as EDITOR_RUNTIME_FINGERPRINT,ph as HouseplanEditorRuntime}; diff --git a/dist/houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js b/dist/houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js similarity index 97% rename from dist/houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js rename to dist/houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js index aa35b134..1bc952c4 100644 --- a/dist/houseplan-assets/houseplan-onboarding-runtime-BiIOObYb.js +++ b/dist/houseplan-assets/houseplan-onboarding-runtime-C0Zl7v7b.js @@ -1,5 +1,5 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{l as t,aS as s,A as e,t as o,c as a,cf as l,cg as i,ch as h,ci as c,aP as n,cj as r,co as p,ck as _,cl as d,cm as u,cn as g,df as m,aO as b,aQ as $,dg as v}from"./houseplan-card-DcH_msOh.js";import{i as f,c as y,e as w,r as D,a as k,b as S,d as C,t as M}from"./backdrop-pick-CXzP7hUG.js";const F=1e3,x=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},T="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";class L{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_help(l){const i=`${l}.aria`,h=t(this.host.hass,this.host._config?.language);return s(h,l)&&s(h,i)?a``:e}_openSpaceDialog(t,s){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===t){const e=this.host._serverCfg.spaces.find(t=>t.id===s);if(!e)return;const o=l(e),a=e.settings?.custom_fill&&"object"==typeof e.settings.custom_fill?i(e.settings.custom_fill):null,p="none"===o.fill?{...a||h,a:0}:a;return void(this.host._spaceDialog={mode:t,spaceId:s,title:e.title,planUrl:e.plan_url||null,planFile:null,source:e.plan_url?"file":"draw",showBorders:o.showBorders,showNames:o.showNames,zeroWallStyle:r(e),displayTouched:!0,hideDecor:o.hideDecor,hideOpenings:o.hideOpenings,roomColor:o.color,roomOpacity:o.opacity,fillMode:"none"===o.fill?"custom":o.fill,customFill:p,glowEnabled:o.glow,bgColor:o.bgColor,bgMode:"static"===e.settings?.bg_mode||"daynight"===e.settings?.bg_mode?e.settings.bg_mode:null,northDeg:n({},e.settings),sunRays:"boolean"==typeof e.settings?.sun_rays?e.settings.sun_rays:null,tempMin:o.tempMin,tempMax:o.tempMax,showLqi:o.showLqi??this.host._config?.show_signal??!0,cardFontScale:o.cardFontScale,labelTemp:o.labelTemp,labelHum:o.labelHum,labelLqi:o.labelLqi,labelLight:o.labelLight,cellCm:Number(e.cell_cm)>0?Number(e.cell_cm):5,cellCmInput:c(Number(e.cell_cm)>0?Number(e.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const e=p(this.host._imperial);this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:{...h,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:d,tempMax:_,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:e,cellCmInput:c(e,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await y(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await w(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return D(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??e}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const s=this.host._spaceDialog,e=s?.saved?.find(s=>s.name===t);if(!s||!e||e.used_by.length||e.url===s.planUrl)return;const o=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._spaceDialog,l=a?.saved?.find(s=>s.name===t);if(o&&a&&l&&l.url===e.url&&l.modified===e.modified&&!l.used_by.length&&l.url!==a.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return a`
    ${this.host._t("space.loading")}
    `;const s=t.saved||[];if(!s.length)return a`
    ${this.host._t("space.no_saved")}
    `;return a`
    +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{l as t,a$ as s,A as e,z as o,c as a,cj as l,ck as i,cl as h,cm as c,aY as n,cn as r,cs as p,co as d,cp as _,cq as u,cr as g,dj as m,aX as b,aZ as $,dk as v}from"./houseplan-card-Csq_DR2Y.js";import{i as f,c as y,e as w,r as D,a as k,b as S,d as C,t as M}from"./backdrop-pick-CEZBr4qx.js";const F=1e3,x=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},T="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";class L{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_help(l){const i=`${l}.aria`,h=t(this.host.hass,this.host._config?.language);return s(h,l)&&s(h,i)?a``:e}_openSpaceDialog(t,s){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===t){const e=this.host._serverCfg.spaces.find(t=>t.id===s);if(!e)return;const o=l(e),a=e.settings?.custom_fill&&"object"==typeof e.settings.custom_fill?i(e.settings.custom_fill):null,p="none"===o.fill?{...a||h,a:0}:a;return void(this.host._spaceDialog={mode:t,spaceId:s,title:e.title,planUrl:e.plan_url||null,planFile:null,source:e.plan_url?"file":"draw",showBorders:o.showBorders,showNames:o.showNames,zeroWallStyle:r(e),displayTouched:!0,hideDecor:o.hideDecor,hideOpenings:o.hideOpenings,roomColor:o.color,roomOpacity:o.opacity,fillMode:"none"===o.fill?"custom":o.fill,customFill:p,glowEnabled:o.glow,bgColor:o.bgColor,bgMode:"static"===e.settings?.bg_mode||"daynight"===e.settings?.bg_mode?e.settings.bg_mode:null,northDeg:n({},e.settings),sunRays:"boolean"==typeof e.settings?.sun_rays?e.settings.sun_rays:null,tempMin:o.tempMin,tempMax:o.tempMax,showLqi:o.showLqi??this.host._config?.show_signal??!0,cardFontScale:o.cardFontScale,labelTemp:o.labelTemp,labelHum:o.labelHum,labelLqi:o.labelLqi,labelLight:o.labelLight,cellCm:Number(e.cell_cm)>0?Number(e.cell_cm):5,cellCmInput:c(Number(e.cell_cm)>0?Number(e.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const e=p(this.host._imperial);this.host._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:{...h,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:_,tempMax:d,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:e,cellCmInput:c(e,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await y(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await w(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return D(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??e}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){const s=this.host._spaceDialog,e=s?.saved?.find(s=>s.name===t);if(!s||!e||e.used_by.length||e.url===s.planUrl)return;const o=await this.host._confirmDanger({key:"delete-plan",kind:"destructive",title:this.host._t("confirm.delete_plan_title"),message:this.host._t("confirm.delete_plan_body"),objectName:t,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),a=this.host._spaceDialog,l=a?.saved?.find(s=>s.name===t);if(o&&a&&l&&l.url===e.url&&l.modified===e.modified&&!l.used_by.length&&l.url!==a.planUrl)try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return a`
    ${this.host._t("space.loading")}
    `;const s=t.saved||[];if(!s.length)return a`
    ${this.host._t("space.no_saved")}
    `;return a`
    ${s.map(s=>{return a`
    @@ -16,7 +16,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef
    `;var e})} -
    `}async _saveConfigNow(){this.host._cfgEpoch++;try{await this.host._writeConfig()}catch(t){const s=t;throw s?.physicalGeometryRolledBack?await this.host._reloadRejectedPhysicalWrite():"conflict"===s?.code&&await this.host._reloadConfigOnly(),t}}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const s="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const e="create"===t.mode?`s${Date.now().toString(36)}`:t.spaceId;let o=null;if("file"===t.source&&t.planFile){o={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:e,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let a=t.savedAspect||null;!o&&"file"===t.source&&t.planUrl&&!a&&this.host._aspectJob&&(a=await this.host._aspectJob||null);const l=this.host._serverCfg;let i=l.spaces.find(t=>t.id===e);if("create"===t.mode)i=k(e,t.title.trim()),l.spaces.push(i);else{if(!i)throw new Error(`space ${e} is gone from the config`);i.title=t.title.trim()}if(!i)throw new Error(`space ${e} is unavailable`);if(o?(i.plan_url=o.url,i.plan_aspect=o.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==i.plan_url&&(i.plan_url=t.planUrl,i.plan_aspect=a),"draw"===t.source&&(i.plan_url=null,i.plan_aspect=null,delete i.plan_x,delete i.plan_y,delete i.plan_scale,delete i.plan_scale_x,delete i.plan_scale_y,delete i.plan_angle),i.settings={...i.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):d,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):_,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},i.zero_wall_style=t.zeroWallStyle,i.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(F,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(i.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length)this._openNextImport();else if(s||this.host._importTotal>0){const t=this.host._importTotal>0;this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),await this.host._requestMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._showToast(this.host._t(s&&!t?"toast.space_added_onboard":"import.done"))}else this.host._showToast(this.host._t("create"===t.mode?"toast.space_added":"toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?await this.host._requestMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._saveNav()))}catch(t){const s=t;"conflict"!==s?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const s=this.host._serverCfg;if(!s)return;const e=s.spaces.find(s=>s.id===t.spaceId);if(!e)return;const o=S(s,this.host._layout||{},t.spaceId||""),a=1===s.spaces.length&&s.spaces[0]?.id===t.spaceId;if(o.count&&!a)return void(this.host._spaceDialog={...t,deleteBlockers:o.count});const l=t.spaceId,i=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:this.host._t("confirm.delete_space_body"),objectName:e.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),h=this.host._spaceDialog,c=this.host._serverCfg;if(!i||!h||"edit"!==h.mode||h.busy||h.spaceId!==l||!c)return;if(!c.spaces.find(t=>t.id===l))return;const n=S(c,this.host._layout||{},l),r=1===c.spaces.length&&c.spaces[0]?.id===l;if(!n.count||r){this.host._spaceDialog={...h,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:l,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[s,e]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(s,e),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===l&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){const s=t;"conflict"!==s?.code&&"space_in_use"!==s?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=S(e,this.host._layout||{},l),s=1===e.spaces.length&&e.spaces[0]?.id===l;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...h,deleteBlockers:n.count}}_startImport(){const t=this.host._importDialog;if(!t)return;const s=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,s.length?(this.host._importQueue=s,this.host._importTotal=s.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();if(void 0===t)return;const s=p(this.host._imperial);this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:d,tempMax:_,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:s,cellCmInput:c(s,this.host._imperial),cellCmTouched:!1,busy:!1}}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this.host._requestMode("plan").then(()=>{this.host._showToast(this.host._t("import.done"))}))}_renderImportDialog(){const t=this.host._importDialog,s=t.floors.filter(t=>t.checked).length;return a``}async _saveConfigNow(){this.host._cfgEpoch++;try{await this.host._writeConfig()}catch(t){const s=t;throw s?.physicalGeometryRolledBack?await this.host._reloadRejectedPhysicalWrite():"conflict"===s?.code&&await this.host._reloadConfigOnly(),t}}async _saveSpaceDialog(){const t=this.host._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this.host._showToast(this.host._t("toast.plan_required"));const s="create"===t.mode&&0===(this.host._serverCfg?.spaces.length||0);this.host._spaceDialog={...t,busy:!0};try{const e="create"===t.mode?`s${Date.now().toString(36)}`:t.spaceId;let o=null;if("file"===t.source&&t.planFile){o={url:(await this.host.hass.callWS({type:"houseplan/plan/set",space_id:e,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let a=t.savedAspect||null;!o&&"file"===t.source&&t.planUrl&&!a&&this.host._aspectJob&&(a=await this.host._aspectJob||null);const l=this.host._serverCfg;let i=l.spaces.find(t=>t.id===e);if("create"===t.mode)i=k(e,t.title.trim()),l.spaces.push(i);else{if(!i)throw new Error(`space ${e} is gone from the config`);i.title=t.title.trim()}if(!i)throw new Error(`space ${e} is unavailable`);if(o?(i.plan_url=o.url,i.plan_aspect=o.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==i.plan_url&&(i.plan_url=t.planUrl,i.plan_aspect=a),"draw"===t.source&&(i.plan_url=null,i.plan_aspect=null,delete i.plan_x,delete i.plan_y,delete i.plan_scale,delete i.plan_scale_x,delete i.plan_scale_y,delete i.plan_angle),i.settings={...i.settings||{},show_borders:t.showBorders,show_names:t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,custom_fill:t.customFill||void 0,glow_enabled:t.glowEnabled,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):_,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):d,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},i.zero_wall_style=t.zeroWallStyle,i.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(F,t.cellCm)):5,await this._saveConfigNow(),this.host._spaceDialog=null,"create"===t.mode&&this.host._commitSpace(i.id),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._importQueue.length)this._openNextImport();else if(s||this.host._importTotal>0){const t=this.host._importTotal>0;this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),await this.host._requestMode("plan"),this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._showToast(this.host._t(s&&!t?"toast.space_added_onboard":"import.done"))}else this.host._showToast(this.host._t("create"===t.mode?"toast.space_added":"toast.space_saved")),"create"===t.mode&&("plan"!==this.host._mode?await this.host._requestMode("plan"):(this.host._tool="draw",this.host._path=[],this.host._cursorPt=null,this.host._primeDrawWallField(),this.host._saveNav()))}catch(t){const s=t;"conflict"!==s?.code&&await this.host._reloadConfigOnly(!0),this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,busy:!1}),this.host._showToast(this.host._t("toast.error",{err:this.host._errText(t)}))}}async _deleteSpace(){const t=this.host._spaceDialog;if(!t||"edit"!==t.mode)return;const s=this.host._serverCfg;if(!s)return;const e=s.spaces.find(s=>s.id===t.spaceId);if(!e)return;const o=S(s,this.host._layout||{},t.spaceId||""),a=1===s.spaces.length&&s.spaces[0]?.id===t.spaceId;if(o.count&&!a)return void(this.host._spaceDialog={...t,deleteBlockers:o.count});const l=t.spaceId,i=await this.host._confirmDanger({key:"delete-space",kind:"destructive",title:this.host._t("confirm.delete_space_title"),message:this.host._t("confirm.delete_space_body"),objectName:e.title,confirmLabel:this.host._t("btn.delete"),cancelLabel:this.host._t("btn.cancel")}),h=this.host._spaceDialog,c=this.host._serverCfg;if(!i||!h||"edit"!==h.mode||h.busy||h.spaceId!==l||!c)return;if(!c.spaces.find(t=>t.id===l))return;const n=S(c,this.host._layout||{},l),r=1===c.spaces.length&&c.spaces[0]?.id===l;if(!n.count||r){this.host._spaceDialog={...h,deleteBlockers:0,busy:!0};try{this.host._saveConfigDebounced.pending()&&this.host._saveConfigDebounced.flush(),this.host._persistLayout.pending()&&this.host._persistLayout.flush(),await this.host._writeChain;const t=await this.host.hass.callWS({type:"houseplan/space/delete",space_id:l,expected_config_rev:this.host._cfgRev,expected_layout_rev:this.host._layoutRev}),[s,e]=await Promise.all([this.host.hass.callWS({type:"houseplan/config/get"}),this.host.hass.callWS({type:"houseplan/layout/get"})]);this.host._adoptStructuralResponses(s,e),this.host._cfgRev=t?.config_rev??this.host._cfgRev,this.host._layoutRev=t?.layout_rev??this.host._layoutRev,this.host._spaceDialog=null,this.host._space===l&&this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||""),this.host._regSignature="",this.host._maybeRebuildDevices(),this.host._showToast(this.host._t("toast.space_deleted"))}catch(t){const s=t;"conflict"!==s?.code&&"space_in_use"!==s?.code||await Promise.all([this.host._reloadConfigOnly(!0),this.host._reloadLayoutOnly()]);const e=this.host._serverCfg;if(this.host._spaceDialog&&e){const t=S(e,this.host._layout||{},l),s=1===e.spaces.length&&e.spaces[0]?.id===l;this.host._spaceDialog={...this.host._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this.host._showToast(this.host._t("toast.delete_failed",{err:this.host._errText(t)}))}}else this.host._spaceDialog={...h,deleteBlockers:n.count}}_startImport(){const t=this.host._importDialog;if(!t)return;const s=t.floors.filter(t=>t.checked).map(t=>t.name);this.host._importDialog=null,s.length?(this.host._importQueue=s,this.host._importTotal=s.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this.host._importQueue.shift();if(void 0===t)return;const s=p(this.host._imperial);this.host._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,...f(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:g,roomOpacity:u,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:_,tempMax:d,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:s,cellCmInput:c(s,this.host._imperial),cellCmTouched:!1,busy:!1}}_skipImport(){this.host._spaceDialog=null,this.host._importQueue.length?this._openNextImport():this.host._importTotal>0&&this.host._model.length&&(this.host._importTotal=0,this.host._commitSpace(this.host._serverCfg.spaces[0]?.id||this.host._space),this.host._requestMode("plan").then(()=>{this.host._showToast(this.host._t("import.done"))}))}_renderImportDialog(){const t=this.host._importDialog,s=t.floors.filter(t=>t.checked).length;return a`this.host._importDialog=null}>
    ${this.host._t("import.hint")}
    diff --git a/dist/houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js b/dist/houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js deleted file mode 100644 index 41064d03..00000000 --- a/dist/houseplan-assets/hp-zigbee-topology-overlay-B7l7_FNi.js +++ /dev/null @@ -1,36 +0,0 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{k as e,A as t,du as i,aw as s,c as o,l as r,j as n}from"./houseplan-card-DcH_msOh.js";import{a,r as h,t as d}from"./zigbee-topology-x104QxRs.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-BFjOXm55.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o` - ${l.length?s``:t} - ${l.map(e=>o`
    `)} - ${p.remoteCount?o`
    ${d(r(this.hass),"remote_count",{n:p.remoteCount})}
    `:t} - `}}v.properties={hass:{attribute:!1},devices:{attribute:!1},registry:{attribute:!1},currentSpace:{type:String,attribute:"current-space"},viewKey:{attribute:!1}},v.styles=n` - :host { position: absolute; inset: 0; z-index: 5; display: block; pointer-events: none; } - svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; } - line { vector-effect: non-scaling-stroke; stroke-linecap: round; } - .halo { - position: absolute; width: calc(var(--device-base-size, 4cqw) * 1.22); - height: calc(var(--device-base-size, 4cqw) * 1.22); transform: translate(-50%, -50%); - box-sizing: border-box; border: 2px solid rgba(120, 190, 220, .82); border-radius: 50%; - box-shadow: 0 0 0 4px rgba(120, 190, 220, .16); - } - .remote { - position: absolute; transform: translate(12px, calc(-100% - 12px)); - border: 1px solid rgba(255,255,255,.7); border-radius: 999px; - padding: 3px 7px; color: #fff; background: rgba(28,31,36,.88); - box-shadow: 0 2px 7px rgba(0,0,0,.28); white-space: nowrap; - font: 600 11px/1.2 system-ui, sans-serif; - } - @media (forced-colors: active) { - line { stroke: Highlight !important; } - .halo { border-color: Highlight; box-shadow: none; } - .remote { color: CanvasText; background: Canvas; border-color: CanvasText; } - } - `,customElements.get("hp-zigbee-topology-overlay")||customElements.define("hp-zigbee-topology-overlay",v);export{v as HpZigbeeTopologyOverlay}; diff --git a/dist/houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js b/dist/houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js new file mode 100644 index 00000000..5f5c79d7 --- /dev/null +++ b/dist/houseplan-assets/hp-zigbee-topology-overlay-CnCZWJj6.js @@ -0,0 +1,36 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{v as e,A as t,dx as i,w as s,c as o,l as r,u as n}from"./houseplan-card-Csq_DR2Y.js";import{a,r as h,t as d}from"./zigbee-topology-D9L1ESh4.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-DUxfg4Z9.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o` + ${l.length?s``:t} + ${l.map(e=>o`
    `)} + ${p.remoteCount?o`
    ${d(r(this.hass),"remote_count",{n:p.remoteCount})}
    `:t} + `}}v.properties={hass:{attribute:!1},devices:{attribute:!1},registry:{attribute:!1},currentSpace:{type:String,attribute:"current-space"},viewKey:{attribute:!1}},v.styles=n` + :host { position: absolute; inset: 0; z-index: 5; display: block; pointer-events: none; } + svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; } + line { vector-effect: non-scaling-stroke; stroke-linecap: round; } + .halo { + position: absolute; width: calc(var(--device-base-size, 4cqw) * 1.22); + height: calc(var(--device-base-size, 4cqw) * 1.22); transform: translate(-50%, -50%); + box-sizing: border-box; border: 2px solid rgba(120, 190, 220, .82); border-radius: 50%; + box-shadow: 0 0 0 4px rgba(120, 190, 220, .16); + } + .remote { + position: absolute; transform: translate(12px, calc(-100% - 12px)); + border: 1px solid rgba(255,255,255,.7); border-radius: 999px; + padding: 3px 7px; color: #fff; background: rgba(28,31,36,.88); + box-shadow: 0 2px 7px rgba(0,0,0,.28); white-space: nowrap; + font: 600 11px/1.2 system-ui, sans-serif; + } + @media (forced-colors: active) { + line { stroke: Highlight !important; } + .halo { border-color: Highlight; box-shadow: none; } + .remote { color: CanvasText; background: Canvas; border-color: CanvasText; } + } + `,customElements.get("hp-zigbee-topology-overlay")||customElements.define("hp-zigbee-topology-overlay",v);export{v as HpZigbeeTopologyOverlay}; diff --git a/dist/houseplan-assets/live-interaction-runtime-C2JvTP_O.js b/dist/houseplan-assets/live-interaction-runtime-C2JvTP_O.js new file mode 100644 index 00000000..19cb04f2 --- /dev/null +++ b/dist/houseplan-assets/live-interaction-runtime-C2JvTP_O.js @@ -0,0 +1 @@ +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{dx as e}from"./houseplan-card-Csq_DR2Y.js";const t=new WeakMap,n=e=>{let n=t.get(e);return n||(n={painted:null,pending:null,raf:0},t.set(e,n)),n},o=e=>`${e.x} ${e.y} ${e.w} ${e.h}`;const r=e=>0===e.translateXPercent&&0===e.translateYPercent&&1===e.scaleX&&1===e.scaleY,a=e=>[e.x,e.y,e.w,e.h].every(Number.isFinite)&&e.w>0&&e.h>0,s=(e,t)=>{if(!t)return e.style.removeProperty("transform"),e.style.removeProperty("transform-origin"),void e.style.removeProperty("will-change");e.style.transformOrigin="0 0",e.style.willChange="transform",e.style.transform=`translate(${t.translateXPercent}%,${t.translateYPercent}%) scale(${t.scaleX},${t.scaleY})`};function i(e,t,n){if(!a(t.view)||!a(n.view)||!a(n.floor))return;const i=o(n.view),l=o(n.floor);for(const t of e.querySelectorAll('[data-hp-live-viewbox="camera"]'))t.setAttribute("viewBox",i);for(const t of e.querySelectorAll('[data-hp-live-viewbox="floor"]'))t.setAttribute("viewBox",l);const c=(f=t.view,u=n.view,{translateXPercent:(f.x-u.x)/u.w*100,translateYPercent:(f.y-u.y)/u.h*100,scaleX:f.w/u.w,scaleY:f.h/u.h});var f,u;for(const t of e.querySelectorAll('[data-hp-live-layer="camera"]'))s(t,r(c)?null:c);const d=e.querySelector("[data-hp-live-zoom]");if(d){d.hidden=n.zoom<=1;const e=d.querySelector("[data-hp-live-zoom-value]");e&&(e.textContent=`${Math.round(100*n.zoom)}%`)}}const l=e=>{const t=e._viewOr(e._baseVb());return{view:{...t},floor:{...e._floorView(t)},zoom:e._zoom}};const c=new WeakMap,f=(e,t,n,o)=>{if(!n)return;const r=document.createElement("span");if(r.className="m",t){r.append(`${t} `);const e=document.createElement("b");e.textContent=n,o&&(e.style.color=o),r.append(e)}else r.textContent=n;e.append(r)},u=(e,t,n)=>{const o=e.querySelector(t);o&&(o.setAttribute("d",n),o.toggleAttribute("hidden",!n))};function d(t){const n=t,o=n.renderRoot;if(!o)return;((t,n)=>{const o=n.querySelector("[data-hp-live-tip]");if(!o)return;const r=t._tip;if(!r)return o.hidden=!0,void o.replaceChildren();o.style.left=`${r.x+12}px`,o.style.top=`${r.y+12}px`,o.replaceChildren();const a=document.createElement("b");a.textContent=r.title,o.append(a),f(o,"",r.meta),f(o,t._t("tip.temp_avg"),null==r.temp?"":`${r.temp}°`),f(o,t._t("tip.hum_avg"),null==r.hum?"":`${r.hum}%`),f(o,t._t("tip.lqi"),null==r.lqi?"":String(r.lqi),null==r.lqi?void 0:e(r.lqi)),o.hidden=!1})(n,o);const r=n._hoverRoom,a=c.get(t),s=r?.space||"",i=r?.room||null;if(a?.room===i&&a?.space===s)return;c.set(t,{room:i,space:s});const l=r&&n._spaceModel(),d=l?n._roomHoverPaths(l):null;u(o,'[data-hp-live-room-hover="fill"]',d?.fillD||""),u(o,'[data-hp-live-room-hover="halo"]',d?.outlineD||""),u(o,'[data-hp-live-room-hover="outline"]',d?.outlineD||"")}function m(e){c.delete(e)}class p{constructor(e){this.host=e,this.deferredHass=!1}hass(e,t,n,o){const r=function(e,t,n){if(e===t)return"none";if(!e||!t||!n)return"structural";if(e.connection!==t.connection||e.entities!==t.entities||e.devices!==t.devices||e.areas!==t.areas||e.themes!==t.themes||e.user!==t.user||e.config!==t.config||e.floors!==t.floors||e.services!==t.services||e.panels!==t.panels||e.language!==t.language||e.locale?.language!==t.locale?.language||e.locale?.number_format!==t.locale?.number_format||e.locale?.time_format!==t.locale?.time_format||e.locale?.date_format!==t.locale?.date_format)return"structural";const o=new Set(["states","connection","entities","devices","areas","themes","user","config","floors","services","panels","language","locale"]);for(const n of new Set([...Object.keys(e),...Object.keys(t)]))if(!o.has(n)&&e[n]!==t[n])return"structural";for(const o of n.entityIds)if(e.states?.[o]!==t.states?.[o])return"state";return"none"}(e,t,n),a="state"===r&&this.active();a&&(this.deferredHass=!0);const s="none"!==r&&!a;return s||this.host._renderLife.observe(e,t,n,o),s}clear(){this.deferredHass=!1}take(){const e=this.deferredHass;return this.deferredHass=!1,e}viewport(e=!1){!function(e,t=!1){const o=e,r=n(e),a=l(o);if(t)return r.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(r.raf),r.raf=0,r.pending=null,r.painted||(r.painted=a),void i(o.renderRoot,r.painted,a);r.pending=a,r.raf||"function"!=typeof requestAnimationFrame||(r.raf=requestAnimationFrame(()=>{r.raf=0;const e=r.pending;r.pending=null;const t=o.renderRoot;e&&t&&(r.painted||(r.painted=e),i(t,r.painted,e))}))}(this.host,e)}hover(){d(this.host)}active(){const e=this.host;return e._pointers.size>0||e._cameraTransition.active||!!e._deviceDrag||!!e._physicalDrag||!!e._physicalRotate||!!e._decorMove||!!e._decorDraft||!!e._dtDrag||!!e._bdDrag||!!e._opDrag||!!e._resize?.dragging}commit(){!function(e){const t=e,o=n(e);o.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(o.raf),o.raf=0,o.pending=null,o.painted=l(t);const r=t.renderRoot;if(r){for(const e of r.querySelectorAll('[data-hp-live-layer="camera"]'))s(e,null);i(r,o.painted,o.painted)}}(this.host),m(this.host),d(this.host)}dispose(){!function(e){const n=t.get(e);n?.raf&&"function"==typeof cancelAnimationFrame&&cancelAnimationFrame(n.raf),t.delete(e)}(this.host),m(this.host)}}export{p as LiveRuntime}; diff --git a/dist/houseplan-assets/zigbee-topology-x104QxRs.js b/dist/houseplan-assets/zigbee-topology-D9L1ESh4.js similarity index 98% rename from dist/houseplan-assets/zigbee-topology-x104QxRs.js rename to dist/houseplan-assets/zigbee-topology-D9L1ESh4.js index 2d346446..237c9c51 100644 --- a/dist/houseplan-assets/zigbee-topology-x104QxRs.js +++ b/dist/houseplan-assets/zigbee-topology-D9L1ESh4.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{s as e,y as n}from"./houseplan-card-DcH_msOh.js";var t={title:"Zigbee links",toggle:"Show Zigbee links when hovering over a device",hint:"Shows observed direct neighbours only. Nothing is fetched while you hover.",admin_only:"Topology data is available to Home Assistant administrators only.",save_first:"Save this setting before loading topology data.",zha:"ZHA",zha_read:"Read ZHA data",zha_hint:"Reads ZHA's cached topology; it does not start a radio scan.",z2m:"Zigbee2MQTT",z2m_topics:"Base topics (one per line)",z2m_update:"Update map",z2m_warning:"A network-map scan can take 10 seconds to 2 minutes and may temporarily reduce Zigbee responsiveness.",status_idle:"Data not loaded",status_loading:"Loading…",status_ready:"Received {time}",status_stale:"Received {time} · stale",status_partial:"Received {time} · some nodes were omitted",status_no_links:"Received {time} · no observed links found",error_permission:"Administrator permission is required.",error_unsupported:"This provider or Home Assistant API is unavailable.",error_timeout:"The provider did not respond in time.",error_invalid_topic:"Check the Zigbee2MQTT base topic.",error_invalid_payload:"The provider returned unsupported topology data.",error_provider:"Topology data could not be loaded.",remote_count:"+{n} in other spaces"};const i={en:t,ru:{title:"Связи Zigbee",toggle:"Показывать связи Zigbee при наведении на устройство",hint:"Показываются только наблюдаемые прямые соседи. Наведение ничего не запрашивает.",admin_only:"Данные топологии доступны только администраторам Home Assistant.",save_first:"Сохраните настройку перед загрузкой данных топологии.",zha:"ZHA",zha_read:"Прочитать данные ZHA",zha_hint:"Читает сохранённую топологию ZHA и не запускает радио-сканирование.",z2m:"Zigbee2MQTT",z2m_topics:"Базовые топики (по одному в строке)",z2m_update:"Обновить карту",z2m_warning:"Сканирование карты сети занимает от 10 секунд до 2 минут и временно может снизить отзывчивость Zigbee.",status_idle:"Данные не загружены",status_loading:"Загрузка…",status_ready:"Получено: {time}",status_stale:"Получено: {time} · данные устарели",status_partial:"Получено: {time} · часть узлов не показана",status_no_links:"Получено: {time} · наблюдаемые связи не найдены",error_permission:"Нужны права администратора.",error_unsupported:"Провайдер или API Home Assistant недоступен.",error_timeout:"Провайдер не ответил вовремя.",error_invalid_topic:"Проверьте базовый топик Zigbee2MQTT.",error_invalid_payload:"Провайдер вернул неподдерживаемые данные топологии.",error_provider:"Не удалось загрузить данные топологии.",remote_count:"+{n} в других пространствах"},de:{title:"Zigbee-Verbindungen",toggle:"Zigbee-Verbindungen beim Darüberfahren über ein Gerät anzeigen",hint:"Es werden nur beobachtete direkte Nachbarn angezeigt. Beim Darüberfahren werden keine Daten abgerufen.",admin_only:"Topologiedaten sind nur für Home-Assistant-Administratoren verfügbar.",save_first:"Speichere diese Einstellung, bevor Topologiedaten geladen werden.",zha:"ZHA",zha_read:"ZHA-Daten lesen",zha_hint:"Liest den ZHA-Topologiecache; es wird kein Funkscan gestartet.",z2m:"Zigbee2MQTT",z2m_topics:"Basistopics (eines pro Zeile)",z2m_update:"Karte aktualisieren",z2m_warning:"Ein Netzwerkscan kann 10 Sekunden bis 2 Minuten dauern und Zigbee vorübergehend verlangsamen.",status_idle:"Daten nicht geladen",status_loading:"Wird geladen…",status_ready:"Empfangen: {time}",status_stale:"Empfangen: {time} · veraltet",status_partial:"Empfangen: {time} · einige Knoten fehlen",status_no_links:"Empfangen: {time} · keine beobachteten Verbindungen gefunden",error_permission:"Administratorrechte sind erforderlich.",error_unsupported:"Dieser Anbieter oder die Home-Assistant-API ist nicht verfügbar.",error_timeout:"Der Anbieter hat nicht rechtzeitig geantwortet.",error_invalid_topic:"Prüfe das Zigbee2MQTT-Basistopic.",error_invalid_payload:"Der Anbieter hat nicht unterstützte Topologiedaten geliefert.",error_provider:"Topologiedaten konnten nicht geladen werden.",remote_count:"+{n} in anderen Bereichen"},fr:{title:"Liens Zigbee",toggle:"Afficher les liens Zigbee au survol d’un appareil",hint:"Seuls les voisins directs observés sont affichés. Le survol ne lance aucune requête.",admin_only:"Les données de topologie sont réservées aux administrateurs Home Assistant.",save_first:"Enregistrez ce réglage avant de charger les données de topologie.",zha:"ZHA",zha_read:"Lire les données ZHA",zha_hint:"Lit la topologie en cache de ZHA sans lancer d’analyse radio.",z2m:"Zigbee2MQTT",z2m_topics:"Topics de base (un par ligne)",z2m_update:"Actualiser la carte",z2m_warning:"L’analyse de la carte réseau peut durer de 10 secondes à 2 minutes et réduire temporairement la réactivité de Zigbee.",status_idle:"Données non chargées",status_loading:"Chargement…",status_ready:"Reçu : {time}",status_stale:"Reçu : {time} · obsolète",status_partial:"Reçu : {time} · certains nœuds sont omis",status_no_links:"Reçu : {time} · aucun lien observé trouvé",error_permission:"Les droits administrateur sont requis.",error_unsupported:"Ce fournisseur ou l’API Home Assistant n’est pas disponible.",error_timeout:"Le fournisseur n’a pas répondu à temps.",error_invalid_topic:"Vérifiez le topic de base Zigbee2MQTT.",error_invalid_payload:"Le fournisseur a renvoyé des données de topologie non prises en charge.",error_provider:"Impossible de charger les données de topologie.",remote_count:"+{n} dans d’autres espaces"}};function r(n,r,o){return e(i[n]?.[r]??t[r]??r,o)}const o=3e5,s=2097152,a=6e3;function d(e){return null!==e&&"object"==typeof e?e:null}function l(e){if("string"!=typeof e&&"number"!=typeof e)return null;let n=String(e).trim().toLowerCase();return n=/^0x[0-9a-f]{16}$/.test(n)?n.slice(2):n.replace(/[:-]/g,""),/^[0-9a-f]{16}$/.test(n)?n:null}function u(e,n,t){return`${e}:${n}:${t}`}function c(e){const n=String(e||"").toLowerCase().replace(/[ _-]/g,"");return n.includes("coordinator")?"coordinator":n.includes("router")?"router":n.includes("enddevice")||"end"===n?"end":"unknown"}function p(e){const n=d(e),t=function(e){const n=Number(e);return Number.isFinite(n)&&n>=0&&n<=255?Math.round(n):void 0}(n?.lqi??n?.linkquality??n?.link_quality),i="string"==typeof n?.relationship?n.relationship.slice(0,40):void 0;return{...void 0===t?{}:{lqi:t},...i?{relationship:i}:{}}}function g(e,n){return Array.isArray(e)&&e.length<=n?e:null}function f(e){if("string"==typeof e)return e.length<=s;try{return JSON.stringify(e).length<=s}catch{return!1}}function _(e,n,t,i,r){if(t===i)return void n.push({code:"self_link",nodeKey:t});const o=t=a){t.push({code:"invalid_payload"});break}const n=d(e),i=l(n?.ieee??n?.ieee_address);if(!i){t.push({code:"invalid_payload",nodeKey:s});continue}const p=u("zha","zha",i);r.has(p)||r.set(p,{key:p,ieee:i,role:c(n?.device_type)}),_(o,t,s,p,e)}else null!=n?.neighbors&&t.push({code:"invalid_payload",nodeKey:s})}return{provider:"zha",instanceId:"zha",obtainedAt:n,freshness:"provider-cache",nodes:[...r.values()],links:[...o.values()],warnings:t}}function m(e,n){const t=d(e),i=l(t?.ieeeAddr??t?.ieee_address??t?.ieee??e);if(i)return i;for(const i of[t?.id,t?.networkAddress,t?.network_address,e])if(null!=i){const e=n.get(String(i));if(e)return e}return null}function v(e,t,i=Date.now()){const r=[],o=n(t)||"invalid",p=f(e)?function(e){let n=e;if("string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}const t=d(n),i=d(t?.data);if(n=i?.value??t?.value??t?.data??n,"string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}return n}(e):null,h=d(p),v=g(h?.nodes,1e3),b=g(h?.links,a);if(!v||!b)return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[],links:[],warnings:[{code:"invalid_payload"}]};const y=new Map,k=new Map;for(const e of v){const n=d(e),t=l(n?.ieeeAddr??n?.ieee_address??n?.ieee);if(!t){r.push({code:"invalid_payload"});continue}const i=u("z2m",o,t);y.set(i,{key:i,ieee:t,role:c(n?.type??n?.device_type),..."boolean"==typeof n?.failed?{available:!n.failed}:{}});for(const e of[n?.id,n?.networkAddress,n?.network_address])null!=e&&k.set(String(e),t)}const z=new Map;for(const e of b){const n=d(e),t=l(n?.sourceIeeeAddr)??m(n?.source,k),i=l(n?.targetIeeeAddr)??m(n?.target,k);if(!t||!i){r.push({code:"invalid_payload"});continue}const s=u("z2m",o,t),a=u("z2m",o,i);y.has(s)||y.set(s,{key:s,ieee:t,role:"unknown"}),y.has(a)||y.set(a,{key:a,ieee:i,role:"unknown"}),_(z,r,s,a,e)}return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[...y.values()],links:[...z.values()],warnings:r}}function b(e){if("string"!=typeof e)return null;const n=l(e);if(n)return n;const t=e.toLowerCase().match(/^zigbee2mqtt(?:_bridge)?_(.+)$/);return t?l(t[1]):null}function y(e,n){if(e.deviceId&&n.devices[e.deviceId])return[e.deviceId];const t=new Set;for(const[i,r]of Object.entries(n.devices)){const n=d(r);(Array.isArray(n?.identifiers)?n.identifiers:[]).some(n=>Array.isArray(n)&&b(n[1])===e.ieee)&&t.add(i)}if(t.size)return[...t];for(const i of Object.values(n.entities)){const n=d(i);"string"==typeof n?.device_id&&"string"==typeof n.unique_id&&(b(n.unique_id)===e.ieee&&t.add(n.device_id))}return[...t]}function k(e){return!e.hidden&&!e.virtual&&"ha_disabled"!==e.bindingStatus?.kind&&"orphaned"!==e.bindingStatus?.kind&&"unverified"!==e.bindingStatus?.kind}function z(e,n,t){const i=new Map,r=[];for(const o of e.nodes){if(!1===o.available){r.push({code:"provider_scan_failure",nodeKey:o.key});continue}const e=y(o,t);if(1!==e.length){r.push({code:"unmatched_device",nodeKey:o.key});continue}const s=e[0];let a=n.filter(e=>k(e)&&"device"===e.bindingKind&&e.bindingRef===s);a.length||(a=n.filter(e=>k(e)&&"entity"===e.bindingKind&&!!e.bindingRef&&t.entities[e.bindingRef]?.device_id===s)),1===a.length?i.set(o.key,{markerId:a[0].id,space:a[0].space}):r.push({code:a.length>1?"ambiguous_placement":"unmatched_device",nodeKey:o.key})}return{placements:i,warnings:r}}function A(e,n,t){return e.map(e=>({topology:e,placements:z(e,n,t).placements}))}function w(e,n,t){const i=new Map,r=new Set;let o=0;for(const{topology:s,placements:a}of e){const e=new Set([...a].filter(([,e])=>e.markerId===t&&e.space===n).map(([e])=>e));if(e.size)for(const d of s.links){const s=e.has(d.a),l=e.has(d.b);if(!s&&!l)continue;const u=a.get(s?d.b:d.a);if(!u||u.markerId===t){o++;continue}if(u.space!==n){r.add(u.markerId);continue}const c=s?d.aToB:d.bToA,p=i.get(u.markerId);(!p||void 0===p.lqi&&void 0!==c?.lqi)&&i.set(u.markerId,{neighborMarkerId:u.markerId,lqi:c?.lqi})}}return{lines:[...i.values()],remoteCount:r.size,omittedCount:o}}export{o as T,A as a,v as b,z as m,h as n,w as r,r as t}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{s as e,I as n}from"./houseplan-card-Csq_DR2Y.js";var t={title:"Zigbee links",toggle:"Show Zigbee links when hovering over a device",hint:"Shows observed direct neighbours only. Nothing is fetched while you hover.",admin_only:"Topology data is available to Home Assistant administrators only.",save_first:"Save this setting before loading topology data.",zha:"ZHA",zha_read:"Read ZHA data",zha_hint:"Reads ZHA's cached topology; it does not start a radio scan.",z2m:"Zigbee2MQTT",z2m_topics:"Base topics (one per line)",z2m_update:"Update map",z2m_warning:"A network-map scan can take 10 seconds to 2 minutes and may temporarily reduce Zigbee responsiveness.",status_idle:"Data not loaded",status_loading:"Loading…",status_ready:"Received {time}",status_stale:"Received {time} · stale",status_partial:"Received {time} · some nodes were omitted",status_no_links:"Received {time} · no observed links found",error_permission:"Administrator permission is required.",error_unsupported:"This provider or Home Assistant API is unavailable.",error_timeout:"The provider did not respond in time.",error_invalid_topic:"Check the Zigbee2MQTT base topic.",error_invalid_payload:"The provider returned unsupported topology data.",error_provider:"Topology data could not be loaded.",remote_count:"+{n} in other spaces"};const i={en:t,ru:{title:"Связи Zigbee",toggle:"Показывать связи Zigbee при наведении на устройство",hint:"Показываются только наблюдаемые прямые соседи. Наведение ничего не запрашивает.",admin_only:"Данные топологии доступны только администраторам Home Assistant.",save_first:"Сохраните настройку перед загрузкой данных топологии.",zha:"ZHA",zha_read:"Прочитать данные ZHA",zha_hint:"Читает сохранённую топологию ZHA и не запускает радио-сканирование.",z2m:"Zigbee2MQTT",z2m_topics:"Базовые топики (по одному в строке)",z2m_update:"Обновить карту",z2m_warning:"Сканирование карты сети занимает от 10 секунд до 2 минут и временно может снизить отзывчивость Zigbee.",status_idle:"Данные не загружены",status_loading:"Загрузка…",status_ready:"Получено: {time}",status_stale:"Получено: {time} · данные устарели",status_partial:"Получено: {time} · часть узлов не показана",status_no_links:"Получено: {time} · наблюдаемые связи не найдены",error_permission:"Нужны права администратора.",error_unsupported:"Провайдер или API Home Assistant недоступен.",error_timeout:"Провайдер не ответил вовремя.",error_invalid_topic:"Проверьте базовый топик Zigbee2MQTT.",error_invalid_payload:"Провайдер вернул неподдерживаемые данные топологии.",error_provider:"Не удалось загрузить данные топологии.",remote_count:"+{n} в других пространствах"},de:{title:"Zigbee-Verbindungen",toggle:"Zigbee-Verbindungen beim Darüberfahren über ein Gerät anzeigen",hint:"Es werden nur beobachtete direkte Nachbarn angezeigt. Beim Darüberfahren werden keine Daten abgerufen.",admin_only:"Topologiedaten sind nur für Home-Assistant-Administratoren verfügbar.",save_first:"Speichere diese Einstellung, bevor Topologiedaten geladen werden.",zha:"ZHA",zha_read:"ZHA-Daten lesen",zha_hint:"Liest den ZHA-Topologiecache; es wird kein Funkscan gestartet.",z2m:"Zigbee2MQTT",z2m_topics:"Basistopics (eines pro Zeile)",z2m_update:"Karte aktualisieren",z2m_warning:"Ein Netzwerkscan kann 10 Sekunden bis 2 Minuten dauern und Zigbee vorübergehend verlangsamen.",status_idle:"Daten nicht geladen",status_loading:"Wird geladen…",status_ready:"Empfangen: {time}",status_stale:"Empfangen: {time} · veraltet",status_partial:"Empfangen: {time} · einige Knoten fehlen",status_no_links:"Empfangen: {time} · keine beobachteten Verbindungen gefunden",error_permission:"Administratorrechte sind erforderlich.",error_unsupported:"Dieser Anbieter oder die Home-Assistant-API ist nicht verfügbar.",error_timeout:"Der Anbieter hat nicht rechtzeitig geantwortet.",error_invalid_topic:"Prüfe das Zigbee2MQTT-Basistopic.",error_invalid_payload:"Der Anbieter hat nicht unterstützte Topologiedaten geliefert.",error_provider:"Topologiedaten konnten nicht geladen werden.",remote_count:"+{n} in anderen Bereichen"},fr:{title:"Liens Zigbee",toggle:"Afficher les liens Zigbee au survol d’un appareil",hint:"Seuls les voisins directs observés sont affichés. Le survol ne lance aucune requête.",admin_only:"Les données de topologie sont réservées aux administrateurs Home Assistant.",save_first:"Enregistrez ce réglage avant de charger les données de topologie.",zha:"ZHA",zha_read:"Lire les données ZHA",zha_hint:"Lit la topologie en cache de ZHA sans lancer d’analyse radio.",z2m:"Zigbee2MQTT",z2m_topics:"Topics de base (un par ligne)",z2m_update:"Actualiser la carte",z2m_warning:"L’analyse de la carte réseau peut durer de 10 secondes à 2 minutes et réduire temporairement la réactivité de Zigbee.",status_idle:"Données non chargées",status_loading:"Chargement…",status_ready:"Reçu : {time}",status_stale:"Reçu : {time} · obsolète",status_partial:"Reçu : {time} · certains nœuds sont omis",status_no_links:"Reçu : {time} · aucun lien observé trouvé",error_permission:"Les droits administrateur sont requis.",error_unsupported:"Ce fournisseur ou l’API Home Assistant n’est pas disponible.",error_timeout:"Le fournisseur n’a pas répondu à temps.",error_invalid_topic:"Vérifiez le topic de base Zigbee2MQTT.",error_invalid_payload:"Le fournisseur a renvoyé des données de topologie non prises en charge.",error_provider:"Impossible de charger les données de topologie.",remote_count:"+{n} dans d’autres espaces"}};function r(n,r,o){return e(i[n]?.[r]??t[r]??r,o)}const o=3e5,s=2097152,a=6e3;function d(e){return null!==e&&"object"==typeof e?e:null}function l(e){if("string"!=typeof e&&"number"!=typeof e)return null;let n=String(e).trim().toLowerCase();return n=/^0x[0-9a-f]{16}$/.test(n)?n.slice(2):n.replace(/[:-]/g,""),/^[0-9a-f]{16}$/.test(n)?n:null}function u(e,n,t){return`${e}:${n}:${t}`}function c(e){const n=String(e||"").toLowerCase().replace(/[ _-]/g,"");return n.includes("coordinator")?"coordinator":n.includes("router")?"router":n.includes("enddevice")||"end"===n?"end":"unknown"}function p(e){const n=d(e),t=function(e){const n=Number(e);return Number.isFinite(n)&&n>=0&&n<=255?Math.round(n):void 0}(n?.lqi??n?.linkquality??n?.link_quality),i="string"==typeof n?.relationship?n.relationship.slice(0,40):void 0;return{...void 0===t?{}:{lqi:t},...i?{relationship:i}:{}}}function g(e,n){return Array.isArray(e)&&e.length<=n?e:null}function f(e){if("string"==typeof e)return e.length<=s;try{return JSON.stringify(e).length<=s}catch{return!1}}function _(e,n,t,i,r){if(t===i)return void n.push({code:"self_link",nodeKey:t});const o=t=a){t.push({code:"invalid_payload"});break}const n=d(e),i=l(n?.ieee??n?.ieee_address);if(!i){t.push({code:"invalid_payload",nodeKey:s});continue}const p=u("zha","zha",i);r.has(p)||r.set(p,{key:p,ieee:i,role:c(n?.device_type)}),_(o,t,s,p,e)}else null!=n?.neighbors&&t.push({code:"invalid_payload",nodeKey:s})}return{provider:"zha",instanceId:"zha",obtainedAt:n,freshness:"provider-cache",nodes:[...r.values()],links:[...o.values()],warnings:t}}function m(e,n){const t=d(e),i=l(t?.ieeeAddr??t?.ieee_address??t?.ieee??e);if(i)return i;for(const i of[t?.id,t?.networkAddress,t?.network_address,e])if(null!=i){const e=n.get(String(i));if(e)return e}return null}function v(e,t,i=Date.now()){const r=[],o=n(t)||"invalid",p=f(e)?function(e){let n=e;if("string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}const t=d(n),i=d(t?.data);if(n=i?.value??t?.value??t?.data??n,"string"==typeof n){if(n.length>s)return null;try{n=JSON.parse(n)}catch{return null}}return n}(e):null,h=d(p),v=g(h?.nodes,1e3),b=g(h?.links,a);if(!v||!b)return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[],links:[],warnings:[{code:"invalid_payload"}]};const y=new Map,k=new Map;for(const e of v){const n=d(e),t=l(n?.ieeeAddr??n?.ieee_address??n?.ieee);if(!t){r.push({code:"invalid_payload"});continue}const i=u("z2m",o,t);y.set(i,{key:i,ieee:t,role:c(n?.type??n?.device_type),..."boolean"==typeof n?.failed?{available:!n.failed}:{}});for(const e of[n?.id,n?.networkAddress,n?.network_address])null!=e&&k.set(String(e),t)}const z=new Map;for(const e of b){const n=d(e),t=l(n?.sourceIeeeAddr)??m(n?.source,k),i=l(n?.targetIeeeAddr)??m(n?.target,k);if(!t||!i){r.push({code:"invalid_payload"});continue}const s=u("z2m",o,t),a=u("z2m",o,i);y.has(s)||y.set(s,{key:s,ieee:t,role:"unknown"}),y.has(a)||y.set(a,{key:a,ieee:i,role:"unknown"}),_(z,r,s,a,e)}return{provider:"z2m",instanceId:o,obtainedAt:i,freshness:"fresh-scan",nodes:[...y.values()],links:[...z.values()],warnings:r}}function b(e){if("string"!=typeof e)return null;const n=l(e);if(n)return n;const t=e.toLowerCase().match(/^zigbee2mqtt(?:_bridge)?_(.+)$/);return t?l(t[1]):null}function y(e,n){if(e.deviceId&&n.devices[e.deviceId])return[e.deviceId];const t=new Set;for(const[i,r]of Object.entries(n.devices)){const n=d(r);(Array.isArray(n?.identifiers)?n.identifiers:[]).some(n=>Array.isArray(n)&&b(n[1])===e.ieee)&&t.add(i)}if(t.size)return[...t];for(const i of Object.values(n.entities)){const n=d(i);"string"==typeof n?.device_id&&"string"==typeof n.unique_id&&(b(n.unique_id)===e.ieee&&t.add(n.device_id))}return[...t]}function k(e){return!e.hidden&&!e.virtual&&"ha_disabled"!==e.bindingStatus?.kind&&"orphaned"!==e.bindingStatus?.kind&&"unverified"!==e.bindingStatus?.kind}function z(e,n,t){const i=new Map,r=[];for(const o of e.nodes){if(!1===o.available){r.push({code:"provider_scan_failure",nodeKey:o.key});continue}const e=y(o,t);if(1!==e.length){r.push({code:"unmatched_device",nodeKey:o.key});continue}const s=e[0];let a=n.filter(e=>k(e)&&"device"===e.bindingKind&&e.bindingRef===s);a.length||(a=n.filter(e=>k(e)&&"entity"===e.bindingKind&&!!e.bindingRef&&t.entities[e.bindingRef]?.device_id===s)),1===a.length?i.set(o.key,{markerId:a[0].id,space:a[0].space}):r.push({code:a.length>1?"ambiguous_placement":"unmatched_device",nodeKey:o.key})}return{placements:i,warnings:r}}function A(e,n,t){return e.map(e=>({topology:e,placements:z(e,n,t).placements}))}function w(e,n,t){const i=new Map,r=new Set;let o=0;for(const{topology:s,placements:a}of e){const e=new Set([...a].filter(([,e])=>e.markerId===t&&e.space===n).map(([e])=>e));if(e.size)for(const d of s.links){const s=e.has(d.a),l=e.has(d.b);if(!s&&!l)continue;const u=a.get(s?d.b:d.a);if(!u||u.markerId===t){o++;continue}if(u.space!==n){r.add(u.markerId);continue}const c=s?d.aToB:d.bToA,p=i.get(u.markerId);(!p||void 0===p.lqi&&void 0!==c?.lqi)&&i.set(u.markerId,{neighborMarkerId:u.markerId,lqi:c?.lqi})}}return{lines:[...i.values()],remoteCount:r.size,omittedCount:o}}export{o as T,A as a,v as b,z as m,h as n,w as r,r as t}; diff --git a/dist/houseplan-assets/zigbee-topology-runtime-BFjOXm55.js b/dist/houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js similarity index 93% rename from dist/houseplan-assets/zigbee-topology-runtime-BFjOXm55.js rename to dist/houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js index 50c3acab..1be58f0b 100644 --- a/dist/houseplan-assets/zigbee-topology-runtime-BFjOXm55.js +++ b/dist/houseplan-assets/zigbee-topology-runtime-DUxfg4Z9.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";import{n as t,b as n}from"./zigbee-topology-x104QxRs.js";import{y as e}from"./houseplan-card-DcH_msOh.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function m(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function g(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,g=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await m(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await m(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot}; +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";import{n as t,b as n}from"./zigbee-topology-D9L1ESh4.js";import{I as e}from"./houseplan-card-Csq_DR2Y.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function m(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function g(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,g=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await m(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await m(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot}; diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index 4eaa406c..a867c73d 100644 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -1 +1 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="7b4d74c1518e5fb1a797d7bcbc7e8b7a5eef380513e51e6901c3c9f537123e91";try{await import("./houseplan-assets/houseplan-card-DcH_msOh.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="71049c6ab3188a66c7112a997ed0f56cdd5e60d90668049e62f53c7c7e022ae7";try{await import("./houseplan-assets/houseplan-card-Csq_DR2Y.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5570581e..8554d7d5 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +- Large plans now keep their heavy room, wall, lighting, decor and device scene + stable during hover, pan, pinch and editor drags, and unrelated Home Assistant + state updates no longer rebuild the full card; interactions remain visually + and functionally unchanged while responding much more smoothly + ([#451](https://github.com/Matysh/houseplan-card/issues/451)). + ## v1.72.0-beta.2 — 2026-09-05 - Double-clicking or double-tapping free plan background in View or kiosk now diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index 47d4e701..6f2dcdf0 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -8,6 +8,13 @@ ## Не выпущено +- На больших планах тяжёлая сцена комнат, стен, света, подложки и устройств + теперь остаётся неизменной во время hover, pan, pinch и перетаскиваний в + редакторах, а посторонние обновления Home Assistant больше не перестраивают + карточку целиком; внешний вид и результат действий не меняются, но отклик + становится заметно плавнее + ([#451](https://github.com/Matysh/houseplan-card/issues/451)). + ## v1.72.0-beta.2 — 2026-09-05 - Двойной клик или двойной тап по свободному фону плана в Просмотре и киоске diff --git a/docs/images/screenshots.json b/docs/images/screenshots.json index 770d873c..fd4d6b75 100644 --- a/docs/images/screenshots.json +++ b/docs/images/screenshots.json @@ -3,7 +3,7 @@ "fixture": "synthetic-only", "chromium": "151.0.7922.34", "oxipng": "oxipng 10.2.0", - "sourceFingerprint": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceFingerprint": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "captureScriptSha256": "cadb8e1bcab9f1dcdd7d75b3b90ddcbaaeb2b8c2a098f575a21f39ff70f5c59c", "command": "npm run build && node demo/docs/capture.mjs", "scenarios": { @@ -15,7 +15,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "80a70361dc18dd0461568df332062e6482c633af5d280954f8b675701418a76d" }, "view-touch": { @@ -26,7 +26,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "4106cc28847047505f46921ff95765d8abdf5b382d9d17d4c5e4ad129dd8f6be" }, "space-create": { @@ -37,7 +37,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "617b51b3648498787b5039980c9f3eceb75ba56ed63a1a20e616bc05bc304362" }, "room-contour-close": { @@ -48,7 +48,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "1dab6cc3b9d1bf7d8c40f0e5137f8c688683c9b7eabc5167da99d41ecfdd5b79" }, "plan-context-tray": { @@ -59,7 +59,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "94ef50762753c0ac6ddc84d2521c4232a3f9ecd89843810d2df61c517592bed7" }, "device-editor": { @@ -70,7 +70,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "7a601769de38aa19c2e280f6ff4cf3695b854b1d1c6d1f8de4e6b5e8f7eb1559" }, "device-display-preview": { @@ -81,7 +81,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "0939875f8631694f4de0ef4fd01032010ede8c7d49fb7c0a857612d4b3afff93" }, "background-editor": { @@ -92,7 +92,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "054170fd9ef45762b602b4d5c9c3b9ea9724858be61af137970c246f485c13bb" }, "room-card": { @@ -103,7 +103,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "2ae4a58853d98e10d12456b2078ec2f6a0b597722c8310d7b016abd2bc42561e" }, "device-info": { @@ -114,7 +114,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "eb4d09faffea5a1b9a958e2b0ed073fd70924271e02a7fa3a73ed9c3f08805a9", + "sourceSha256": "eb0b787c06ef06671e953d560312329da5a3774087b9c740e2a4717ea8a78427", "imageSha256": "cb37f7eefd936f98ef44969b21dfe8e40ee27d1f558abe8d53c0223f6488b8a6" } }, diff --git a/docs/reviews/CODE-REVIEW-451-r1.md b/docs/reviews/CODE-REVIEW-451-r1.md new file mode 100644 index 00000000..fdc18e1f --- /dev/null +++ b/docs/reviews/CODE-REVIEW-451-r1.md @@ -0,0 +1,301 @@ +# CODE-REVIEW-451-r1 + +- **Issue:** #451 «План подтормаживает: диагностика на каждый кадр, реактивная камера, отсутствие фильтра HA-тиков» +- **Этап:** код-ревью, заход r1 (первый действительно проведённый — предыдущая попытка не читала код: ветка не ребейзилась на `dev` без конфликта, цикл не израсходован) +- **Материал:** `git log --oneline origin/dev..HEAD` / `git diff origin/dev...HEAD`, SHA `1850eb18b5e8f0813f9d2efb50694dc4ca1fff26` +- **ТЗ:** `docs/specs/451-render-performance.md` (полный трек, `SPEC-REVIEW-451-r2` зелёный) +- **Вердикт:** см. итог в конце документа + +## Скоуп диффа + +75 файлов, +3947/‑1166 (без учёта `dist/**`/`custom_components/.../frontend/**`, класс D). Новые модули: +`src/render-invalidation.ts`, `src/houseplan-render-lifecycle.ts`, `src/live-viewport.ts`, +`src/live-hover.ts`, `src/live-editor.ts`, `src/live-interaction-runtime.ts`, +`src/pointer-move-queue.ts`, `src/interaction-types.ts`. Изменены `src/houseplan-card.ts` +(overridden `requestUpdate`, diagnostics, terminal reconciliation), `src/houseplan-editor-runtime.ts` +(pointer-move очереди для plan/decor/backdrop/opening/resize), `src/render-device-snapshot.ts` +(`entityIds`), `src/zigbee-topology-overlay-bridge.ts` (live-layer маркер). Плюс performance harness +(`demo/benchmark_large_house.mjs`, `budgets-large-house-interaction.json`, `card-contract.mjs`, +`evaluate.mjs`, `performance.yml`) и точечные правки существующих `demo/smoke_*.mjs` под новую разметку +`data-hp-live-*`. Геометрия/модель стен, `layout`, `marker.space`, `open_spans` не тронуты — гейт +`npm run invariants` не требуется, что подтверждается и явным «Non-scope» ТЗ. + +## Как проверялось + +| Гейт | Команда | Результат | +|---|---|---| +| typecheck | `npx tsc --noEmit` | зелёный | +| unit | `npm test` | 1956 tests, 1955 pass, 1 skip, 0 fail | +| build + бандл | `npm run build && cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` + `diff -rq` на `houseplan-assets` | совпадают байт-в-байт | +| docs fingerprint | `node scripts/check-docs.mjs` (обязателен — diff трогает `src/**`) | «Documentation checks passed (7 files, 12 external links)» | +| any-гейт | не запускал отдельно — новые файлы не содержат `any` (проверено чтением), `npm test`/`typecheck` зелёные | проверено чтением | +| smoke-select | `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | 98 прямых совпадений + 36 слабых из 221 (см. `smoke-select.txt`) | +| smoke (почти полный набор) | все 134 файла из прямых+слабых совпадений, `node demo/smoke_.mjs` по одному | 133 OK, 1 нестабильный (см. ниже, не регрессия) | +| golden | `npm run golden:verify` | 153/153 сценариев `passed`, 0 diff — доказывает AC9 (pixel-equivalence) | +| performance (структурные assert'ы) | `npm run benchmark:large-house-interaction -- --samples=7 --warmups=1` | завершился без throw — структурный контракт (0 full render на hover/pan/editor move, 1 terminal, 0 diagnostics scans) подтверждён на этом SHA | +| performance (абсолютные потолки) | `npm run benchmark:compare -- --absolute-only --budgets=demo/performance/budgets-large-house-interaction.json --candidate=` | **6 проверок красные** — см. находку H1 | +| backend | не запускал — диф не трогает `custom_components/**/*.py` (проверено чтением: `git diff --name-only` не содержит `.py`) | проверено чтением | +| invariants | не запускал — диф не трогает геометрию/`layout`/`marker.space`/`open_spans` (проверено чтением) | проверено чтением | + +Не прогонял полный `demo/smoke_*.mjs` (все ~221) и полный `performance.yml` (`Full Performance`, +парный base/candidate на двух чекаутах) — первое избыточно (134 из 221 уже покрывают все «прямые» и +«слабые» совпадения smoke-select, второе требует парного окружения, которое не гейт код-ревью +(PROCESS.md §8: «полные наборы — предрелizный гейт»). Раздельный прогон `--absolute-only` (см. выше) +компенсирует это для абсолютных потолков нового профиля — и именно он нашёл H1. + +## Находки + +### H1 (High, блокирует) — новый performance-профиль `large-house-interaction-v1` не проходит собственный бюджет + +**AC10** требует: «новый interaction profile проходит все structural, absolute и base-relative checks +§11». Структурные проверки (0 full render на hover/pan/camera/editor move, ровно 1 terminal, 0 +diagnostics scans, стабильный heavy DOM, ошибка совмещения SVG/HTML ≤1px) — подтверждены, профиль не +бросает исключение. Но абсолютные потолки, которые сам же диф вводит в +`demo/performance/budgets-large-house-interaction.json`, не выполняются при фактическом прогоне. + +**Воспроизведение** (детерминировано, не связано со скоростью машины — см. ниже): + +``` +npm run benchmark:large-house-interaction -- --samples=7 --warmups=1 --output=/tmp/interaction-review7.json +npm run benchmark:compare -- --absolute-only \ + --budgets=demo/performance/budgets-large-house-interaction.json \ + --candidate=/tmp/interaction-review7.json --output=/tmp/interaction-compare7.json +``` + +Результат — 6 красных строк: + +``` +❌ timing.interactionSeriesMs.median | 3501.5 | 3000 +❌ timing.editorSeriesMs.median | 1841.9 | 750 +❌ longTask.editorSeries.maxSingleMs | 878 | 150 +❌ longTask.editorSeries.countP95 | 4 | 3 +❌ longTask.editorSeries.totalP95Ms | 1633 | 300 +❌ cache.entries.cleanFloor | 140 | 100 +``` + +`cache.entries.cleanFloor` — счётчик записей `Map`, не таймінг, машинно-независим. Проверено на **двух** +независимых прогонах (`--samples=3` и `--samples=7`), значение **140 во всех 10 сэмплах без единого +отклонения**. Для контроля прогнан немодифицированный `large-house-v1` (существует и на `dev`, диф его +не трогает): + +``` +npm run benchmark:large-house -- --samples=3 --warmups=1 --output=/tmp/base-profile-review.json +# cleanFloor: 100, 100, 100 — ровно документированный потолок (demo/performance/README.md: +# «the reviewed fixture warms exactly 100 deterministic room/physical-body entries») +``` + +Значит переполнение специфично именно для новой `editor series` части `large-house-interaction-v1` +(последовательность `_setMode('plan')` → `_tool='resize'` → cancel → `_setMode('decor')` → +`_setMode('view')`), которую этот же диф добавил в `demo/benchmark_large_house.mjs`. Разница ровно ++40 записей (два «лишних» полных прохода флор-кэша сверх документированного одного) коррелирует с тем, +что тайминговые метрики именно `editorSeriesMs`/`longTask.editorSeries.*` превышены в 2,4× и более — +похоже на одну и ту же причину (лишний(е) пересчёт(ы) `_cleanFloor`/`floorMinusBodies` при переключении +режима редактора), а не на независимый шум. + +Попытка воспроизвести на упрощённой ручной фикстуре (`page.evaluate` с прямым вызовом `_setMode` без +полного цикла реального pointer-move, который использует бенчмарк) рост `_cleanFloorCache.size` не +показала — то есть причина не в самом факте смены режима, а где-то в фактической +resize/decor-транзакции бенчмарка (drag-preview → cancel) или в стечении с реальными pointer-событиями. +Дальнейшая локализация — задача автора; здесь важен сам факт: **AC10 не выполнен по написанному самим +автором для этой задачи бюджету**, а не только гипотеза о причине. + +Тайминговые превышения (`interactionSeriesMs`, `editorSeriesMs`, `longTask.editorSeries.*`) в отличие +от `cleanFloor` теоретически могут быть частично усилены более медленной/разделяемой машиной ревьюера +против выделенной машины владельца (`demo/performance/README.md`: «Local Windows checkout is the +day-to-day environment... local report is diagnostic only»), но margin (1842 мс против потолка 750 мс, +878 мс одна long task против потолка 150 мс) для чистого шума великоват, и коррелирует с детерминированным +`cleanFloor`-превышением. Автору стоит перепроверить оба числа на каноническом Linux CI парном прогоне +(`performance.yml`), но независимо от результата **cache-count уже доказан красным и машинно не +объясним** — это не «непрогнанный дорогой гейт», это прогнанный и красный. + +**Почему High, а не Medium.** Задача, ценность 10/10 и P1, явно посвящена именно тому, чтобы новый +перф-контракт «краснел сам по себе, а не только на фоне предыдущего коммита» (issue, раздел «Почему это +не поймал перф-гейт»). Проверка, которую AC10 называет своим доказательством, при фактическом запуске +красная. Это не вопрос «недостаточно полно проверили», а обнаруженный красный автотест по собственному, +введённому этим же диффом контракту — ровно то, что код-ревью обязано поймать (PROCESS.md §2.7: +«ревьюер отвечает за AC… либо доказан автотестом… либо разобран по коду»). Фикс — в скоупе (тот же +`demo/benchmark_large_house.mjs`/бюджет), отдельный issue не заводится. + +### M1 (Medium, в скоупе) — обещанный в самом ТЗ «targeted production-bundle smoke» не создан + +ТЗ §13.2 и §14 (план реализации, п. 5) явно называют отдельный артефакт: «Targeted `demo/smoke_*.mjs` — +production-bundle render-count и interaction matrix», инструментирующий full-render count, lightweight +paints, diagnostics scans, heavy node identity, writes на PRODUCTION-бандле (не на perf-фикстуре) — и +именно это заявлено как доказательство AC5/AC6/AC7/AC8 (`smoke` в их графе доказательства). + +`git diff origin/dev...HEAD --diff-filter=A` показывает: новых файлов `demo/smoke_*.mjs` нет вовсе. +Изменения в существующих смоках (`smoke_decor.mjs`, `smoke_furniture.mjs`, `smoke_decor_text.mjs`, +`smoke_resize_pointer_real_plan.mjs`, `smoke_pan_any_zoom.mjs`, `smoke_room_fit.mjs`, +`smoke_infinite_canvas.mjs`, `smoke_opening_measure.mjs`, `smoke_partition_openings.mjs`, +`smoke_drag_bounds.mjs`, `smoke_room_tooltip_toggle.mjs`, `smoke_ux_fixes.mjs`) — точечная подгонка под +новую разметку `data-hp-live-*`/тайминг RAF, ни один не считает full-render/diagnostics-scan счётчики. + +Единственное место, где эти структурные гарантии вообще проверяются — `demo/benchmark_large_house.mjs +--profile=large-house-interaction-v1`, которое подключено только к job'е `performance.yml` → «Full +Performance», запускаемой (см. `demo/performance/README.md`, «CI contracts») **на промоушен в `main`, +раз в неделю и по ручному диспатчу** — не на каждый push в `dev`. Блокирующий `performance_smoke` в +`validate.yml` меряет только `large-house-glow-overlay-v1`, этого профиля не касается. При принятой в +проекте модели прямых коммитов в `dev` без PR (AGENTS.md) это означает: следующий обычный коммит, +который случайно вернёт diagnostics-scan на каждый рендер или полный render на hover, **не будет +пойман никаким гейтом до следующего еженедельного/promotion-прогона** — то есть тем самым классом +слепого пятна, ради которого заведён #451 (issue, раздел «Почему это не поймал перф-гейт»: «гейт +исправно подтверждал, что "не стало хуже", пока пользователь смотрел на 451-миллисекундные фризы»). + +Фикс в скоупе: добавить обещанный `demo/smoke_*.mjs` (инструментирование `_renderBody`/ +`_bindingStatus` аналогично тому, что уже сделано внутри `benchmark_large_house.mjs`, но на +production-бандле через `demo/serve.mjs`, в постоянно запускаемом наборе). + +### M2 (Medium, в скоупе) — нет мутанта в `scripts/mutation-gate.mjs` для новых защитных механизмов + +PROCESS.md §2.7: «Мутант в `scripts/mutation-gate.mjs` обязателен, когда защита живёт в продуктовом +коде и проверяется дорогим гейтом (смок, бэкенд, golden): там ревьюер не воспроизведёт отрицательный +прогон второй раз». `git diff origin/dev...HEAD -- scripts/mutation-gate.mjs` — пусто, ни одной новой +записи. + +Часть новых защит — чистые юниты, и для них я сам прогнал мутацию и увидел красный тест (см. «Таблица +доказательств» ниже: `classifyHassRenderChange`, `RenderLifecycle.diagnostics`) — этого достаточно, +отдельный мутант не нужен. Но интеграционная проводка в `houseplan-card.ts` (переопределённый +`requestUpdate`, `_flushHa`/terminal reconciliation, RAF-coalesced camera/hover/editor paints) — +юнитами не покрыта и проверяется только дорогим Playwright-бенчмарком (тем же, что в M1/H1). Без +постоянного мутанта в следующем раунде (после того, как H1 будет исправлен и профиль перестанет +краснеть) ревьюер снова не сможет дёшево доказать «тест умеет падать» — придётся заново гонять +браузерный бенчмарк вручную, как это делал я. + +### Low — известная нестабильность `smoke_smooth_zoom.mjs` под нагрузкой (не регрессия, к сведению) + +При параллельном прогоне 134 смоков (4 конкурентных пакета) `demo/smoke_smooth_zoom.mjs` дал один +красный результат (`wheelRetargetsRunningTween`/`wheelHasIntermediateFrames`/`wheelStreamPersistsOnce` += false). Проверил отдельно: последовательно (без конкуренции за CPU) — 11/11 прогонов зелёные, как на +этой ветке, так и на чистом `origin/dev`. Затем воспроизвёл красный результат **на обеих** ветках +одинаково при явной конкуренции (4 параллельных `smoke_smooth_zoom.mjs` + 3 тяжёлых смока рядом: 2-3 из +4 падали и на `dev`, и на PR). Вывод: это существующая до #451 чувствительность теста к CPU-контеншну +(reliance на реальные `requestAnimationFrame` тайминги), не регрессия этой задачи. Не блокирует, в +скоуп #451 не входит, отдельный issue не заводится (не находка, а объяснённая флакиность). + +## Что проверено и корректно + +- **AC1** (HA intake ≠ visual invalidation): `requestUpdate('hass', …)` в `houseplan-card.ts` + делегирует решение `LiveRuntime.hass()` → `classifyHassRenderChange()`; при `'none'`/deferred-`'state'` + intake (`intakeHass`) всё равно выполняется синхронно через `RenderLifecycle.observe`. Доказано + юнитом `test/houseplan-render-lifecycle.test.mjs` («HA intake runs once even when an unrelated visual + update is skipped») + мутацией (см. ниже). +- **AC2** (dependency classifier): `src/render-invalidation.ts`. Структурные top-level поля (`entities`, + `devices`, `themes`, `locale.*`, неизвестный новый ключ) — fail-open на `'structural'`; только `states` + сравнивается по dependency-scoped `entityIds`. Доказано `test/render-invalidation.test.mjs` (4/4) + + мутацией: убрал ранний `return 'none'`→заменил на безусловный `return 'state'` в скомпилированном + `test-build/render-invalidation.js` (не в `src/**`, генерируемый файл) — тест + «an unrelated HA state row does not invalidate the plan frame» покраснел (`expected 'none', actual + 'state'`), затем восстановил файл, тест снова зелёный. +- **AC3** (intake переживает пропущенный рендер): dependency projection в `_captureRenderDeviceSnapshot` + (houseplan-card.ts:4563+) собирает entity/device/area из bindings, room temp/hum source, opening + entity refs, live-text, `sun.sun`, vacuum source — доказано чтением и подтверждено проходом смоков + `smoke_linked_virtual_light`, `smoke_static_icon`, `smoke_cover_no_plate`, + `smoke_cover_plate_precedence`, `smoke_entity_parent_dedup`, `smoke_yellow_principle` (все зелёные). +- **AC4** (diagnostics cache): `RenderLifecycle.diagnostics()`/`invalidate()` в + `houseplan-render-lifecycle.ts`. Единственный вызов `houseplanDiagnostics()`-подобной логики теперь и + в `_renderBody` (через `this._renderLife.diagnostics(...)`), и в публичном `houseplanDiagnostics()` — + один источник (см. «Одно число — один источник» ниже). Доказано юнитом («diagnostics scan is cached + and invalidated by tracked state presence») + мутацией: заменил guard `if (this.diagnosticsCache)` на + `if (false && this.diagnosticsCache)` в `test-build/houseplan-render-lifecycle.js` — тест покраснел + (`2 !== 1`, ожидался один скан вместо двух), восстановил, снова зелёный. +- **AC5/AC6/AC7** (0 full render на hover/pan/pinch/camera/editor move, 1 terminal, deferred-relevant-tick + last-wins): структурные assert'ы внутри `demo/benchmark_large_house.mjs` (throw при нарушении) + подтверждены самостоятельным прогоном на этом SHA — все условия выполнены (`irrelevantFullRenders:0`, + `hoverFullRenders:0`, `panMoveFullRenders:[0,0,0,0]`/`terminalFullRenders:[1,1,1,1]`, + `cameraMoveFullRenders:0`/`cameraTerminalFullRenders:1`, `editorMoveFullRenders:[0,0,0]`, + `relevantDuringGestureFullRenders:0` при `relevantDuringGestureIntakes:3`, `diagnosticsScans:0`, + `heavyNodeStable:true`, `overlayErrorPx:0.01`). Тайминги того же прогона см. H1. +- **AC8** (coalescing сохраняет конечную координату/commit/cancel): `pointer-move-queue.ts` + (`queueMicrotask`, гарантированно опустошается до следующего браузерного события) + подключение во + всех точках (`_pointerMove`/device, `_physicalMove`, `_rszMove`, `_bdMove`, `_opPointerMove`, `_dtMove`, + `_decorMoveUpdate`, `_markupMove`) с явным `flush` на terminal (`up`) и `cancel` на отмене. Доказано + юнитом `test/live-editor.test.mjs` («pointer move queue is event-turn coalesced, last-wins and + flushable») + прогоном затронутых production-bundle смоков (`smoke_furniture`, `smoke_decor`, + `smoke_decor_text`, `smoke_drag_bounds`, `smoke_partition_openings`, `smoke_opening_measure`, + `smoke_resize_pointer_real_plan` — все зелёные, включая ровно те сценарии shift-snap/magnet/rotate, + которые чувствительны к потере промежуточного состояния). +- **AC9** (pixel-equivalence): `npm run golden:verify` — 153/153 `passed`, 0 diff, включая + `large-house-zoom-040/250-dark`, `large-house-warm-remount-dark`, junction-серию, hover-серию, + dark/light/RU/EN варианты. Прямое доказательство отсутствия непреднамеренных визуальных изменений. +- **AC11** (implementation-loop gates): typecheck/test/build зелёные (таблица выше). +- **AC12** (schema/backend/i18n/dependencies не изменены): `git diff --name-only` не содержит + `.py`/`i18n`/`translations`/`manifest.json`/`hacs.json` — проверено чтением. Новые кэши + (`RenderLifecycle.diagnosticsCache`, `LiveRuntime`/`live-viewport`/`live-hover`/`live-editor` state) — + все `WeakMap`, ключ — сам инстанс карточки; `disconnectedCallback` вызывает + `_liveRt?.dispose()` и `_editorRuntime?._disposeLiveEditor()` (cancel RAF, restore hidden/transparent + DOM, `cancelHouseplanPointerMove`) — bounded per card, очищаются при disconnect, независимы между + несколькими карточками на дашборде (WeakMap не даёт общий каталог). +- **AC13** (документация): `demo/performance/README.md` описывает новый профиль, сценарии, структурные + assertions, абсолютные потолки, local diagnostics-команду; `docs/CHANGELOG.md`/`docs/CHANGELOG.ru.md` + обновлены в том же коммите `c0d61ca3` (`User-Visible: yes`), формулировка «внешний вид и результат + действий не меняются» соответствует AC9/non-scope. +- **Одно число — один источник**: `houseplanDiagnostics()` (публичный support-report) и внутренний + render-path теперь читают один и тот же `RenderLifecycle.diagnostics()` — раньше было два независимых + прохода по маркерам с одинаковой логикой (потенциальное расхождение при будущей правке одного из них), + теперь физически один вызов на одну инвалидацию (доказано юнитом «scans === 1» выше). Другой видимый + дважды параметр — zoom badge (`Math.round(zoom*100)%`) и `_zoom`/`_view`: и DOM-бейдж + (`live-viewport.ts:paintLiveViewport`), и canonical-поле читают один и тот же `frameOf(host)` — + `host._zoom`, без второго независимого округления. +- **Трейлеры**: все 10 коммитов несут `Issue: #451`; ровно один `User-Visible: yes` + (`c0d61ca3`, «fix: keep large-plan interactions off the full render path») с обоими changelog в том + же коммите; остальные `User-Visible: no` (документация/фикс-ап/ребилд бандла) — корректно. +- **Touch/pen/kiosk/fixed-floor контракт** (§9 ТЗ): не даёт отдельной ветки поведения — подтверждено + прохождением `smoke_isometric_live_touch`, `smoke_kiosk`, `smoke_kiosk_pan_lock`, `smoke_fixed_floor`, + `smoke_touch_tips`, `smoke_grid_scale_invariance` (все зелёные) плюс golden dark/light/RU/EN сценарии. + +## Чего не проверял + +- Полный `demo/smoke_*.mjs` (все ~221) — прогнал 134 (все «прямые» и «слабые» совпадения + smoke-select.mjs), сознательно не прогонял оставшиеся ~87 без связи с диффом (полный набор — + предрелизный гейт, PROCESS.md §8). +- Парный `performance.yml` («Full Performance», base vs candidate на двух чекаутах, реальный Chromium + CI-раннер владельца) — недоступен в этом окружении; вместо него прогнал `--absolute-only` (см. H1, + нашёл реальную красноту) и структурные assert'ы бенчмарка напрямую. Относительное (base-vs-candidate) + сравнение не проверено — вероятно, тоже покраснеет из-за той же причины, что и абсолютное, но это не + проверено напрямую. +- Backend (`python -m pytest tests_backend`) — не запускал, диф не касается `custom_components/**/*.py` + (проверено чтением diff, не исполнением). +- `npm run invariants` — не запускал, диф не касается геометрии/`layout`/`marker.space`/`open_spans` + (проверено чтением diff и Non-scope раздела ТЗ). +- Точная локализация причины лишних 40 записей `cleanFloor` (H1) — воспроизвёл и доказал факт, не нашёл + точную строку кода; это оставлено автору при исправлении H1. +- Ручная проверка в реальном браузере (вне headless Playwright) и на настоящей Home Assistant — вне + доступного окружения; полагался на golden/smoke/unit/performance гейты. + +## Материал раунда + +- Ветка: `issue/451-render-performance` +- SHA материала: `1850eb18b5e8f0813f9d2efb50694dc4ca1fff26` +- Диапазон: `origin/dev...HEAD` (10 коммитов, `029aba6b`..`1850eb18`) +- ТЗ: `docs/specs/451-render-performance.md`, ревью — `docs/reviews/SPEC-REVIEW-451-r2.md` (зелёное) + +## Итог + +**Вердикт: красный.** Одна High-находка (H1 — заявленный самим диффом перф-контракт `AC10` красный при +фактическом прогоне `compare.mjs --absolute-only` по собственному новому бюджету, доказано +детерминированным счётчиком `cache.entries.cleanFloor: 140 vs 100` плюс коррелирующими превышениями +таймингов `editorSeriesMs`/`longTask.editorSeries.*`) блокирует переход. Плюс две Medium-находки в +скоупе (M1 — не создан обещанный ТЗ `demo/smoke_*.mjs` для render-count на production-бандле, M2 — нет +мутанта в `scripts/mutation-gate.mjs` для интеграционной проводки, проверяемой только дорогим +бенчмарком). Сама архитектура решения (разделение intake/visual invalidation, dependency classifier, +diagnostics cache, RAF-coalesced live-слои камеры/hover/редакторов, pointer-move очереди) сделана +аккуратно и проверяемо: юнит-защиты действительно ловят мутацию, structural assertions бенчмарка +действительно бросают исключение при нарушении контракта, golden/smoke/typecheck/build/docs — все +зелёные. Возврат автору — на исправление конкретно H1 (и желательно M1/M2 в том же цикле, раз без +High они и так были бы Medium-в-скоупе), без расширения скоупа задачи. + +--- + + + +## Материал раунда + +- Ветка: `issue/451-render-performance`, коммит `1850eb18b5e8` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `30dcff478a0287b209be4c3066972158d6e6b539` + ``` + git log --all --format='%H %T' | grep 30dcff478a02 + ``` +- ТЗ `docs/specs/451-render-performance.md`, блоб `7c323a29110974aae369077214b9e2a74d9387c1` + ``` + git log --all --find-object=7c323a29110974aae369077214b9e2a74d9387c1 -- docs/specs/451-render-performance.md + ``` diff --git a/docs/reviews/CODE-REVIEW-451-r2.md b/docs/reviews/CODE-REVIEW-451-r2.md new file mode 100644 index 00000000..91c54a3e --- /dev/null +++ b/docs/reviews/CODE-REVIEW-451-r2.md @@ -0,0 +1,253 @@ +# CODE-REVIEW-451-r2 + +- **Issue:** #451 «План подтормаживает: диагностика на каждый кадр, реактивная камера, отсутствие фильтра HA-тиков» +- **Этап:** код-ревью, заход r2 +- **Материал:** `git log --oneline origin/dev..HEAD` / `git diff origin/dev...HEAD`, точный SHA `cb68492c990b27e91c35130fa3a77806bd157c5e` (ветка `issue/451-render-performance`) +- **Предыдущий раунд:** `CODE-REVIEW-451-r1`, SHA `1850eb18b5e8f0813f9d2efb50694dc4ca1fff26`, вердикт красный, High 1 / Medium 2 (SHA назван в самом документе r1, `Материал:`; в комментарии-вердикте issue SHA не упомянут — это несоответствие оформления, не находка по существу) +- **ТЗ:** `docs/specs/451-render-performance.md` (полный трек, `SPEC-REVIEW-451-r2` зелёный) +- **Вердикт:** см. итог в конце документа + +## Дельта раунда + +`git diff 1850eb18..cb68492c` — 8 источников кода/скриптов вне `dist/**`/`custom_components/.../frontend/**` +(класс D, бинарные копии бандла синхронизированы автоматически и не разбираются построчно): + +| Файл | Изменение | +|---|---| +| `src/junction-limits.ts` | новая экспортируемая `junctionLimitViolations()` — общая реализация full/affected-room проверки, вынесенная из `houseplan-editor-runtime.ts`; добавлен `lightweight`-режим (переиспользует переданный `multiWallNodes`, не пересобирает `wallBodiesGeometry`) | +| `src/houseplan-editor-runtime.ts` | `_junctionLimitViolations` делегирует в новую функцию с `roomIds`; `_rszProjectPreview` больше не вызывает `_rszSpaceCandidateGeometry`/`_checkSpacePhysicalGeometry` на каждый move — использует lightweight junction-check, ограниченную `changedRoomIds`; полная проверка (`_rszCandidateRenderable`) осталась только в `finish()`; `_rszAcceptPreview`/`_rszCancelDrag` управляют `_cfgEpoch`/`_physicalBodiesCache.key` точнее; `_renderResizeLayer`/`_renderOpenings`/`_renderDecorLayer` получили фильтры `roomIds`/`onlyIds`/`onlyId` | +| `src/live-editor.ts` | resize-preview теперь рисует только затронутые стены (`resizePreviewWalls`, читает `room.poly`/`wall.a|b|cm|key`, строит `WallEdgeBody` сама), а не полный `_renderWallBodies`; decor-preview делает прозрачным только перетаскиваемый шейп, а не весь `.decorlayer` | +| `src/houseplan-card.ts` | `_terminalFrame` (0/1/2): `_renderBody()` возвращает `noChange` на кадре отмены resize/decor, если состояние байт-в-байт совпадает с исходным; `requestUpdate` сбрасывает флаг при любом именованном свойстве | +| `src/resize-controller.ts` + `test/resize-controller.test.mjs` | `cancel()` возвращает `restoreEpoch`, чтобы `_rszCancelDrag` мог откатить `_cfgEpoch`, а не только `_wallUnionCache` | +| `demo/benchmark_large_house.mjs` | измерение `longTask.editorSeries` разбито на три отдельных окна (по одному на resize/resize/decor-часть), вместо одного окна на весь editor-сценарий | +| `scripts/bundle-budget.mjs` | `INITIAL_VIEW_GZIP_CEILING` 297 000 → 298 000, с обоснованием | +| `scripts/mutation-gate.mjs` + `demo/smoke_render_invalidation.mjs` (новый) | закрывают M1/M2 r1 | + +Геометрия и ссылки на неё прямо затронуты (`junction-limits.ts`, чтение `wall.a/b/cm/key`, +`room.poly`, `multiWallNodesForGeometry`, `wallBodiesGeometry`, `innerContourForRoom`) — разбор +не сокращается до «только заявленных находок», гейты по геометрии прогнаны (см. ниже). + +## Как проверялось + +| Гейт | Команда | Результат | +|---|---|---| +| typecheck | `npx tsc --noEmit` | зелёный | +| unit | `npm test` | 1956 tests, 1955 pass, 1 skip, 0 fail | +| build + бандл | `npm run build && node scripts/bundle-sync.mjs` + `cmp`/`diff -rq` трёх копий | совпадают байт-в-байт | +| docs fingerprint | `node scripts/check-docs.mjs --external` (обязателен — diff трогает `src/**`) | **красный**, см. H1 | +| any-гейт | `node scripts/no-new-any.mjs --base f8b3445d --head cb68492c` (та же пара, что использует CI-джоба этого пуша) | **красный**, см. H2 | +| invariants | `npm run invariants -- --config <экспорт large-house фикстуры>` (diff трогает геометрию/wall-thickness) | «Инварианты выполнены: ссылки разрешимы, записи толщины находятся» | +| smoke-select (по дельте) | `node scripts/smoke-select.mjs --base 1850eb18 --head cb68492c` | 58 прямых, 27 слабых, 1 зарегистрированная связь (`smoke_real_plan_masonry.mjs` ← `wallBodiesGeometry`) | +| smoke (все прямые + зарегистрированная + 5 смежных по физической геометрии) | 66 файлов, по одному `node demo/smoke_.mjs` (список — в приложении к комментарию) | 64 OK, 1 красный воспроизводимо (`smoke_room_resize.mjs`, см. H3), 1 красный только под конкуренцией за CPU при последовательном прогоне всех 66 (`smoke_edit_walk.mjs`; повторный изолированный прогон — 6/6 OK, тот же класс флакиности, что и `smoke_smooth_zoom` в r1, не регрессия) | +| golden | `npm run golden:verify` | 153/153, 0 diff | +| performance (структурные assert'ы) | `npm run benchmark:large-house-interaction -- --samples=7 --warmups=1` (×3 независимых прогона) | завершается без throw каждый раз | +| performance (абсолютные потолки) | `npm run benchmark:compare -- --absolute-only --budgets=demo/performance/budgets-large-house-interaction.json` на всех 3 прогонах | `cache.entries.cleanFloor` = 100/100 во всех трёх (совпадает с заявленным автором числом, H1 r1 в этой части закрыт); но в каждом из 3 прогонов минимум один тайминговый чек красный (`longTask.editorSeries.maxSingleMs` 231–324 vs 150 во всех трёх; изредка также `timing.interactionSeriesMs.median` и `longTask.editorSeries.totalP95Ms`) — см. «Под вопросом» ниже | +| backend | не запускал — диф не трогает `custom_components/**/*.py` (проверено чтением: `git diff 1850eb18..cb68492c --name-only` не содержит `.py`) | проверено чтением | +| мутация | `node scripts/mutation-gate.mjs --id=render-invalidation-renders-irrelevant-ha` | «поймано 1 из 1» — M2 r1 подтверждён рабочим | + +CI на этом же SHA (`cb68492c`, run `33918076536`) независимо подтверждает H1 и H2: джоба +«Предполётные проверки» красная на `DOCS: failure`, джоба «Фронтенд: типы, юниты, мутанты, +синхрон бандла» красная на «Новый код не добавляет any: 11» — обе с теми же файлами/строками, +что и локальный прогон ниже. Всё, что стоит за этими двумя джобами по конвейеру (браузерные +смоки, перф-смок, golden), было **skipped**, то есть CI не подтверждает и не опровергает +performance-часть отдельно от того, что нашёл я локально. + +## Находки + +### H1 (High, блокирует) — отпечаток скриншотов документации устарел, `docs`-джоба красная + +`node scripts/check-docs.mjs --external` (та же команда, что в `validate.yml:59`) завершается: + +``` +ERROR screenshot source fingerprint is stale; run npm run build && node demo/docs/capture.mjs +``` + +`visualFingerprint()` считается по всему `src/**` (#245: версия не в счёт, но код — да), а +раунд r2 меняет `src/houseplan-card.ts`, `src/houseplan-editor-runtime.ts`, +`src/junction-limits.ts`, `src/live-editor.ts`, `src/resize-controller.ts` без сопутствующего +`npm run build && node demo/docs/capture.mjs`. Автор прогонял этот гейт в r1 (документ +`CODE-REVIEW-451-r1` фиксирует «Documentation checks passed»), но в комментарии о фиксе r1 +(`Исправления по CODE-REVIEW r1`, 2026-09-04T20:48:54Z) `check-docs` не упомянут вовсе — гейт +пропущен именно там, где diff тронул `src/**` заново. Ровно этот класс пропуска уже дважды +оставлял `dev` с красной `docs`-джобой до следующей задачи (#230, #234 → #237); здесь он пойман +до слияния. Фикс не меняет ни одного пикселя (перерисовка внутренняя), поэтому обновление — +чисто отпечаток, `npm run build && node demo/docs/capture.mjs`. + +Подтверждено независимо: CI-прогон `33918076536` на этом же SHA, джоба «Предполётные проверки», +`DOCS: failure`. + +### H2 (High, блокирует) — новый код добавляет 11 непрокомментированных `any`, гейт красный + +`node scripts/no-new-any.mjs --base f8b3445d --head cb68492c` (пара база/head, которую +использовала CI-джоба этого пуша) находит 11 новых явных `any` на добавленных строках без +обоснования `// any-ok: …`: + +``` +src/houseplan-card.ts:7792, src/junction-limits.ts:51,54,57,73, +src/live-editor.ts:61,62,65,157,163,164 +``` + +Все — в коде, который непосредственно реализует ТЗ этого раунда (сигнатуры +`junctionLimitViolations`/`LiveEditorHost`, читающие `config`/`space`/`room` как `any`). Часть +можно типизировать через уже существующие интерфейсы (`ServerConfig`, `SpaceModel`), часть — +обосновать комментарием на той же строке; гейт не требует нулевого `any` в принципе (в `src/**` +их 1034 в 49 файлах), а требует явного решения по каждой НОВОЙ строке. + +Подтверждено независимо: тот же CI-прогон, джоба «Фронтенд: типы, юниты, мутанты, синхрон +бандла», шаг «Новый код не добавляет any», идентичный список строк. + +### H3 (High, блокирует) — resize потерял видимую fail-closed реакцию на невозможную геометрию посреди жеста + +`node demo/smoke_room_resize.mjs` падает воспроизводимо (проверено дважды подряд, изолированно +и в общем прогоне — одинаково): + +``` +FAILED (2): + - safe_resize.preflight_visible_reason: expected true, got false + - safe_resize.preflight_reason_once: expected 1, got 0 +``` + +Смок (существовал до #451, не новый) подставляет `card._checkSpacePhysicalGeometry = () => ({ +ok: false })` и ожидает, что **во время** протяжки (`pointermove`, до `pointerup`) карточка +покажет тост «last safe position/последн…» ровно один раз. До этого раунда `_rszProjectPreview` +на каждый move вызывал `_rszSpaceCandidateGeometry()` → `_checkSpacePhysicalGeometry()` и +отклонял проекцию с `reason: 'physical-geometry'`, если проверка проваливалась — именно так +жест «останавливался на последней безопасной позиции» с видимой причиной. Правка H1-r1 убрала +этот вызов из `_rszProjectPreview` целиком (осталась только lightweight junction-limit проверка, +ограниченная `changedRoomIds`, — другой класс проверки: буквенные лимиты узлов/клиренса, а не +собираемость геометрии стен). Полная проверка (`_checkSpacePhysicalGeometry`) осталась только в +`_rszCandidateRenderable`, вызываемой из `_rszUp()` → `finish()` — то есть **только по +pointerup**, с общим `resize.commit_failed` тостом вместо специфичного отказа посреди жеста. + +Строка `// #329 AC7a: a step that would ADD a junction-limit violation is never projected, so +the drag stops at the last allowed position…` (houseplan-editor-runtime.ts:3657) осталась в коде +рядом с местом, где ссылалась на удалённую проверку — комментарий теперь описывает только +junction-limit ветку, а не physical-geometry, но текст не обновлён и вводит в заблуждение. + +Ни один юнит-тест не покрывает новую `junctionLimitViolations()`/lightweight-режим отдельно — +единственная проверка всей цепочки жила в этом смоке, и правка её не заметила именно потому, что +`npm test` эту ветку не касается (только браузерный смок её и держал). Это тот же структурный +разрыв, который M2 r1 уже называл для другой части того же диффа: дорогой гейт — единственный +свидетель контракта. + +**Почему High.** Регресс в контракте, существовавшем до #451 (не новая функциональность этого +issue), ловится существующим (не добавленным этим PR) тестом, воспроизводится детерминированно. +`User-Visible: no` в трейлере коммита `cb68492c` в этом свете неточен: сценарий редкий +(конкурентное структурное изменение во время жеста), но при его наступлении пользователь раньше +видел объяснение и жест останавливался, а теперь видит только общий отказ после отпускания +курсора — то есть разное поведение, а не «незаметная» оптимизация. + +## Под вопросом, не поднимаю до находки + +`longTask.editorSeries.maxSingleMs` (потолок 150 мс) красный во всех 3 независимых прогонах +`--samples=7 --warmups=1` на этом SHA (231, 324, 231 мс), при этом `cache.entries.cleanFloor` +(машинно-независимый счётчик, из-за которого r1 был красным) держит ровно 100/100 во всех трёх — +то есть структурная часть H1-r1 закрыта чисто, а тайминговая часть держится у самой границы с +переменным исходом (в одном из трёх прогонов краснели ещё `timing.interactionSeriesMs.median` и +`longTask.editorSeries.totalP95Ms`, в двух других — нет). Отличие от r1: там margin был ~6× +(878 мс против потолка 150) и детерминирован; здесь ~1.5–2× и не детерминирован — характернее +для шумной/разделяемой машины ревьюера, чем для структурного регресса (`demo/performance/README.md` +прямо называет локальный прогон диагностическим). CI не добрался до перф-джобы на этом SHA +(skipped из-за H1/H2), так что канонического парного Linux-сравнения по этому раунду ещё нет ни +у кого. Не поднимаю до отдельной находки, но и не закрываю: автору нужен чистый прогон +`performance.yml` (или локальный на менее нагруженной машине) после того, как H1/H2/H3 будут +исправлены, прежде чем считать AC10 доказанным на этом SHA. + +## Закрытие раунда r1 + +| Находка r1 | Чем закрыта | Где видно | +|---|---|---| +| H1 (перф-бюджет `large-house-interaction-v1` красный, включая машинно-независимый `cache.entries.cleanFloor` 140≠100) | Resize-preview больше не строит канонический union/full-room clearance на каждый move — только lightweight junction-check по `changedRoomIds` | `cache.entries.cleanFloor` = 100/100 в трёх независимых прогонах этого раунда (см. таблицу выше); тайминговая часть бюджета — см. «Под вопросом» | +| M1 (нет production-smoke на full-render/diagnostics-scan счётчики) | Добавлен `demo/smoke_render_invalidation.mjs` | файл существует, `node demo/smoke_render_invalidation.mjs` → OK | +| M2 (нет мутанта в `scripts/mutation-gate.mjs` для интеграционной проводки) | Добавлен мутант `render-invalidation-renders-irrelevant-ha` | `node scripts/mutation-gate.mjs --id=render-invalidation-renders-irrelevant-ha` → «поймано 1 из 1» | + +## Унаследовано из r1 (не перепроверялось в этом раунде) + +Со ссылкой на `CODE-REVIEW-451-r1` на SHA `1850eb18`, за пределами того, чего касается дельта +`1850eb18..cb68492c`: + +- AC1 (HA intake ≠ visual invalidation), AC2 (dependency classifier), AC3 (terminal + reconciliation вне resize/decor cancel-ветки), AC4 (RAF-coalesced hover/camera paths вне + editor-preview), AC9 (pixel-equivalence) — код этих путей дельтой не тронут; повторно + перечитан не был. AC9 переподтверждён косвенно свежим прогоном `golden:verify` (153/153) в + этом раунде, остальное — по r1. +- Скоуп диффа (75 файлов на SHA r1) вне восьми файлов, изменённых в этом раунде — не + перечитывался повторно. +- Backend/invariants-обоснование «диф не трогает `custom_components/**/*.py`» — верно и для + дельты этого раунда (см. таблицу выше), проверено заново, не только унаследовано. +- Трейлеры `Issue:`/`User-Visible:` коммита r1 (`f8b3445d`, `1850eb18` и предыдущие) — не + перепроверялись; трейлеры коммита `cb68492c` проверены заново (см. H3 про неточность + `User-Visible: no`). + +## Что проверено и корректно + +- `_rszCandidateRenderable`/`_checkSpacePhysicalGeometry` остаются полной, дорогой проверкой на + `finish()` (пойнтерап) — итоговый коммит геометрии по-прежнему фейлится закрыто; регресс H3 + касается только видимой обратной связи ПОСРЕДИ жеста, не итоговой записи (доказано: `roomPoly` + до/после в смоке не меняется — `preflight_no_commit` в том же смоке зелёный). +- `resize-controller.ts`: `epochBefore`/`restoreEpoch` для cancel корректно откатывают + `_cfgEpoch` только при совпадении `snapshotIdentity`, иначе `null` — соответствующие тесты + `test/resize-controller.test.mjs` (оба сценария) проходят. +- `_terminalFrame` в `houseplan-card.ts`: сбрасывается на любую именованную реактивную запись + (`requestUpdate(name, …)` с `name !== undefined`) до того, как `_renderBody()` может вернуть + `noChange` — то есть «проглотить» кадр может только сам вызывающий cancel-путь, а не случайное + внешнее свойство; `npm test` (1955/1955) и `demo/smoke_render_invalidation.mjs` не показали + расхождений DOM. +- Единственный источник числа: диф не вводит новых пользовательски видимых величин (внутренний + перф-рефакторинг), `npm test` включает `test/single-source-numbers.test.mjs` — зелёный. +- `INITIAL_VIEW_GZIP_CEILING` 297 000 → 298 000 обоснован в комментарии тем же коммитом, общий + бюджет 300 000 и долг #367 не изменены — проверено чтением `scripts/bundle-budget.mjs`. +- Инварианты модели (`npm run invariants`) на конфиге из `demo/fixtures/large-house.mjs`: + «ссылки разрешимы, записи толщины находятся» — новая `junctionLimitViolations()` не потеряла + соответствие ключей записей толщины решёточным рёбрам на этой фикстуре. + +## Чего не проверял и почему + +- Полный набор `demo/smoke_*.mjs` (222 файла) — избыточно: 58 прямых + 1 зарегистрированная + связь смок-селектора плюс 5 смежных по физической геометрии (`smoke_wall_junctions`, + `smoke_wall_union_isolation`, `smoke_near_orthogonal_junction`, + `smoke_multiwall_strip_containment`, `smoke_glow_geometry_resilience`) дают 64/66 живых + прогонов по затронутым модулям — уже нашли реальный регресс (H3). Оставшиеся 27 «слабых» + совпадений (общее имя `_mode`/`_baseVb`/`_physicalBodiesCache`/`cellCm`/`NORM_W`) не гонял: + все пять физически-геометрических смоков из этого же семейства прошли, дополнительный сигнал + от оставшихся маловероятен, а полный набор — предрелизный гейт (PROCESS.md §8), не гейт + ревью. +- Полный `performance.yml` (`Full Performance`, парный base/candidate на двух чекаутах) — CI это + не запускала на этом SHA (skipped из-за H1/H2), у меня нет второго чекаута под парное + сравнение; заменено тройным `--absolute-only` прогоном на кандидате (см. «Под вопросом»). +- `python -m pytest tests_backend` — диф не трогает `custom_components/**/*.py` (проверено + чтением `git diff --name-only`). +- Мутация для новой `junctionLimitViolations()`/lightweight-режима отдельно — не потребовалась: + H3 уже показывает красный тест по этому пути; добавлять мутант к сломанному коду + преждевременно, стоит сделать после фикса H3, иначе он зафиксирует текущее (неверное) + поведение. +- Повторный запуск `smoke_edit_walk.mjs` больше двух раз для статистики флакиности — не + требовалось: тот же класс поведения (падает только под конкуренцией за CPU при плотном + последовательном прогоне 66 браузерных смоков подряд, чисто 6/6 в изоляции), что уже описан и + принят как некритичный в r1 для `smoke_smooth_zoom`. + +## Итог + +**Вердикт: красный.** Три High-находки, все в скоупе задачи и чинятся в ней же (без отдельных +issue): H1 — обновить отпечаток документации (`npm run build && node demo/docs/capture.mjs`), +H2 — типизировать или обосновать 11 новых `any`, H3 — вернуть full physical-geometry preflight +(или эквивалентную защиту) в путь `_rszProjectPreview`, чтобы посреди resize-жеста +невозможная геометрия снова отклонялась видимо, а не только по `pointerup`. После фикса — +чистый повторный прогон `npm run benchmark:large-house-interaction`/`compare` для снятия вопроса +из раздела «Под вопросом». + +--- + + + +## Материал раунда + +- Ветка: `issue/451-render-performance`, коммит `cb68492c990b` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `aaa8519cee54bf2a81f9e922e583d42d7f594ab4` + ``` + git log --all --format='%H %T' | grep aaa8519cee54 + ``` +- ТЗ `docs/specs/451-render-performance.md`, блоб `7c323a29110974aae369077214b9e2a74d9387c1` + ``` + git log --all --find-object=7c323a29110974aae369077214b9e2a74d9387c1 -- docs/specs/451-render-performance.md + ``` diff --git a/docs/reviews/CODE-REVIEW-451-r3.md b/docs/reviews/CODE-REVIEW-451-r3.md new file mode 100644 index 00000000..24b81a4e --- /dev/null +++ b/docs/reviews/CODE-REVIEW-451-r3.md @@ -0,0 +1,223 @@ +# CODE-REVIEW #451 — заход r3 + +- **Issue:** #451 — «План тормозит: диагностика считается на каждый кадр, перетаскивание перерисовывает всё, нет фильтра обновлений» +- **Этап:** код-ревью (PROCESS.md §2.7) +- **Заход:** r3 · блокирующих циклов до этого раунда: 2/4 +- **Материал раунда:** ветка `issue/451-render-performance`, `HEAD = 444562e47cb746dc1c7d740b66b2f832ca02f064` + (сверено `git rev-parse HEAD` непосредственно перед выводом, дерево чистое, PROCESS.md §2.7/#312) +- **Дельта раунда:** `git diff cb68492c990b27e91c35130fa3a77806bd157c5e..444562e4` — SHA взят из машинного блока + «Материал раунда» документа `docs/reviews/CODE-REVIEW-451-r2.md` (дерево `aaa8519cee54…`, сверено + `git cat-file -p cb68492c^{tree}` — совпадает). Файлы дельты (без `dist/**` и бандла стенда): + `src/houseplan-card.ts`, `src/houseplan-editor-runtime.ts`, `src/junction-limits.ts`, + `src/live-editor.ts`, `src/resize-controller.ts`, `test/resize-controller.test.mjs`, + `docs/images/screenshots.json`, `docs/reviews/CODE-REVIEW-451-r2.md`. + +## Скоуп раунда + +r2 (красный, High:3) закончился на коммите `cb68492c` (диагностика — 8 файлов). С тех пор в ветку +легли 7 коммитов: `fe03eae2`, `8d787530`, `4b6b4abc`, `a7bee0f5`, `d513f13a` (документ r2), +`046efe96`, `444562e4`. Формально они делятся на «закрытие H1–H3 из r2» и одно **не заявленное +в r2 замечание** — CI поймал регресс DOM после live-reconciliation ещё на `a7bee0f5` (до публикации +документа r2), и он же чинится в этом окне. Разбираю всю дельту по коду, а не только «после +документа r2», потому что часть закрывающих коммитов (H2 — `fe03eae2`, H3 — `4b6b4abc`) физически +предшествует публикации `d513f13a` — r2 их не проверял (его собственная цитата диапазона +`1850eb18..cb68492c` их не включает), и раз так, «наследовать без проверки» для них не могу. + +## Как проверялось + +Дельта не геометрию персистентного формата, а раннее прекращение/typing нескольких вызовов и одну +внутреннюю функцию (`resizeLivePreflightAllowed`). Прогнал сам, не полагаясь на цитаты из комментариев: + +| Команда | Результат | +|---|---| +| `npm run build` (после — `git status --short`) | зелёный, дерево не изменилось — бандл воспроизводим байт-в-байт | +| `npm run bundle:sync` | зелёный, стенд для браузерных смоков синхронизирован | +| `npm test` | 1956 passed / 0 failed / 1 skipped (совпадает с заявленным) | +| `node scripts/check-docs.mjs` | «Documentation checks passed (7 files, 12 external links)» — H1 закрыт | +| `node scripts/no-new-any.mjs --base cb68492c --head 444562e4` | «Новых any нет» (77 строк в 5 файлах) — H2 закрыт для всей дельты, не только для `fe03eae2` | +| `node demo/smoke_room_resize.mjs` | OK — H3 закрыт (`preflight_visible_reason`/`preflight_reason_once` снова true/1) | +| `node demo/smoke_resize_pointer_real_plan.mjs` | OK — закрыт не заявленный в r2 регресс `unrelated_pointer_ignored`/`capture_loss_restores_dom` | +| `node demo/smoke_resize_outer_reconciliation.mjs` | OK | +| `node demo/smoke_resize_audit_1550.mjs` | OK | +| `node demo/smoke_resize_inner_dimensions.mjs` | OK | +| `node demo/smoke_resize_labels.mjs` | OK | +| `node demo/smoke_resize_wall_thickness.mjs` | OK | +| `node demo/smoke_junction_limits.mjs` | OK | +| `node demo/smoke_wall_key_roundtrip.mjs` | OK | +| `node demo/smoke_bg_color.mjs` | OK | +| `node demo/smoke_pan_any_zoom.mjs` | OK | + +Плюс независимая архивная проверка через `gh`: CI `33920551036` (SHA `a7bee0f5`, до публикации r2) +показал `Смоки в браузере (шард 3 из 3): failure` с точными именами +`resize_pointer.unrelated_pointer_ignored` / `resize_pointer.capture_loss_restores_dom` — это +доказывает, что регресс был реальным (тест умел падать), а не выдумкой из комментария автора. +Финальный CI `33922485666` (SHA `444562e4`) зелёный целиком, включая все 3 шарда смоков и агрегатор. + +`node scripts/smoke-select.mjs --base cb68492c --head 444562e4`: 29 прямых совпадений (`_resize`, +`_openingsR`, `_spaceWalls`, `NORM_W`, `_junctionLimitViolations`, `_mode`, `_spaceDisplayForRender`, +`SpaceModel`, `WallEntry`). Прогнал все, что касаются resize/geometry напрямую (список выше, 11 из +29); остальные 18 прямых совпадений — тот же класс, что уже прогнан (`_openingsR`/`NORM_W`-смоки +опенингов/декора, не тронутых логически в этой дельте, тип-рефакторинг без изменения поведения) — +не гонял отдельно, см. «Чего не проверял». + +## Закрытие r2 + +| Находка r2 | Чем закрыта | Где видно | +|---|---|---| +| **H1** (`check-docs --external` красный, отпечаток скриншотов устарел) | Каноническая Linux-съёмка на точном SHA, обновлён только source fingerprint | `444562e4`; сам прогнал `node scripts/check-docs.mjs` на HEAD — зелёный | +| **H2** (11 новых `any` в `houseplan-card.ts:7792`, `junction-limits.ts:51,54,57,73`, `live-editor.ts:61,62,65,157,163,164`) | Типизация `_junctionLimitViolations`/`junctionLimitViolations` через новый экспортируемый `JunctionSharedGeometry` (`Pick \| {status:'lightweight',...}`), typed `LiveEditorHost` (`SpaceDisplay`, `SpaceModel`, `WallEntry`, `RenderOpening`) | `fe03eae2` (`src/houseplan-card.ts`, `src/houseplan-editor-runtime.ts`, `src/junction-limits.ts`, `src/live-editor.ts`); сам прогнал `no-new-any.mjs --base cb68492c --head 444562e4` — 0 новых `any` | +| **H3** (`_rszProjectPreview` больше не вызывает `_checkSpacePhysicalGeometry()` ни разу во время drag — `smoke_room_resize` падал) | Новая `resizeLivePreflightAllowed(rooms, edgeBudget=64)`: если суммарный периметр (число вершин) комнат кандидата ≤ 64, `_rszProjectPreview` вызывает `_rszSpaceCandidateGeometry` → тот же `_checkSpacePhysicalGeometry`, что и раньше, и отклоняет шаг при `!ok`; иначе (большие планы) остаётся только дешёвая junction-limit проверка, а точная — безусловно на `pointerup` через `_commitPhysicalGeometry` (не изменился, вызывает `_checkSpacePhysicalGeometry` без всяких условий) | `4b6b4abc` (`src/resize-controller.ts:resizeLivePreflightAllowed`, `src/houseplan-editor-runtime.ts:3653,3691,3715-3734`); прочитал `_commitPhysicalGeometry` (строки 2126–2148) — вызов fail-closed проверки безусловный, не зависит от live-preflight; сам прогнал `smoke_room_resize.mjs` — OK | + +Дополнительно (не было заявлено r2 находкой, но CI поймал регресс на `a7bee0f5` — SHA, на котором +физически лежал код r2 к моменту анализа, хотя r2 цитировал более ранний `cb68492c`): смоки +`resize_pointer.unrelated_pointer_ignored`/`capture_loss_restores_dom` красные. Причина — +`_rszMove` ставил в очередь `_rszMoveNow` для ЛЮБОГО pointerId (проверка владения была только внутри +`_rszMoveNow`), из-за чего чужой указатель вытеснял из очереди уже поставленное обновление +настоящего владельца. Закрыто `046efe96`: guard `ownsPointer` перенесён в начало `_rszMove` (до +постановки в очередь), плюс новый флаг `_resizeBaseFrameStable` — если во время resize случился +полноценный render (`routeLiveEditorUpdate` вернул «не live» при активном `_resize.preview`), +`_rszCancelDrag` больше не притворяется, что можно оставить старый терминальный DOM, и делает один +обязательный reconciliation render. Сам прогнал `smoke_resize_pointer_real_plan.mjs` — OK; независимо +подтверждено CI `33922485666` (все 3 шарда зелёные). + +## Унаследовано из r1 (через r2, без повторной проверки) + +Эта дельта не касается доказательной базы следующих пунктов r1/r2 — принимаю как есть: + +- AC1–AC2, AC7 (разделение intake/visual invalidation, dependency projection, last-wins HA во + время жеста) — не тронуты дельтой r2→r3 (файлы фильтра `hass`/dependency classifier в диффе + `cb68492c..444562e4` отсутствуют). Документ: `CODE-REVIEW-451-r2.md`, SHA `cb68492c`. +- AC4 (diagnostics cache) — не тронут этой дельтой. +- AC9 (golden/canonical screenshots) — r2 подтвердил 153/153 без диффов на `cb68492c`; резерв + CI на `444562e4` («Переиспользование: это дерево уже проверено» → успех, «Golden-кадры» → skipped) + подтверждает, что источник для golden не менялся с последнего полного прогона. Отдельно не + перепрогонял: дельта r2→r3 не меняет settled-состояние (см. ниже про resize-preflight — эффект + только во время активного жеста, до golden-снимка дело не доходит). +- Инварианты модели по всем моделям проекта — часть `npm test` (сам прогнал на HEAD, зелёный); + конфиг-специфичная команда `npm run invariants -- --config …` не нужна отдельно: дельта не меняет + персистентную форму (`walls[]`, `wall_segments`, `marker.space`, `open_spans`) — только момент + вызова validation-функции и типизацию сигнатур. +- `INITIAL_VIEW_GZIP_CEILING` 297000→298000 — обоснование и бюджет не тронуты этой дельтой (только + `bundle-budget.mjs` логика из r1, файл не в диффе r2→r3). + +## Находки + +### M1 — не заявленный в ТЗ постоянный отказ от live-preflight на больших планах, противоречит собственному changelog (в скоупе, чинится в этой же задаче) + +**Файлы:** `src/resize-controller.ts:18-27` (`resizeLivePreflightAllowed`), +`src/houseplan-editor-runtime.ts:3653` (использование в `_rszProjectPreview`). + +Фикс H3 (`4b6b4abc`) не просто вернул прежнее поведение — он расколол его по размеру плана. +Для «bounded» контуров (суммарно ≤ 64 вершин комнат текущего пространства) во время resize-жеста +работает тот же точный `_checkSpacePhysicalGeometry()`, что был до #451: невозможная геометрия сразу +отклоняется, тост «последняя безопасная позиция» показывается посреди жеста. Для «больших» планов +(> 64 вершин) эта проверка **безусловно выключена во время жеста** — живёт только дешёвая +junction-limit проверка (углы/валентность), а точная геометрия проверяется один раз, на `pointerup` +(это безопасно для данных — commit всё ещё fail-closed, я прочитал `_commitPhysicalGeometry` +построчно, — но не для обратной связи пользователю). + +Порог не абстрактный: фикстура `demo/fixtures/large-house.mjs`, на которой считается сам бюджет +`large-house-interaction-v1` (тот самый профиль, ради которого выключалась проверка), — это 20 +прямоугольных комнат по 4 вершины = **80 вершин**, то есть уже выше порога 64. А персона из ТЗ +(«администратор дома... средний или большой план», issue: «5 пространств... 8 комнат в текущем +пространстве... средний по размеру план, не рекорд») — это ровно тот случай, для которого порог +скорее всего будет превышен, если комнаты не прямоугольные (8 комнат × 8 вершин = 64, граница +ровно на пороге). + +Почему это находка, а не техническая деталь реализации: + +1. **ТЗ прямо запрещает это как область изменения.** §5 Non-scope: «изменение hit areas, snap + tolerance, grid, gesture thresholds, animation duration/easing, click/double-click/long-press + или commit/cancel semantics» не входит в задачу. §2: «Преднамеренных визуальных изменений нет. + После завершения любого жеста канонический итог и итоговый кадр совпадают с текущим поведением» + — про итоговый кадр это верно (commit не изменился), но живая обратная связь **во время** жеста + для больших планов теперь другая, и это не техническая деталь — это то, что видит пользователь. + Ни §16 (риски), ни §18 (что можно менять свободно без владельца) не упоминают эту развилку. +2. **Прямо противоречит changelog, который уже в ветке.** `docs/CHANGELOG.md`/`.ru.md` (коммит + `c0d61ca3`, до r1) заявляют: «interactions remain visually and functionally unchanged» / + «внешний вид и результат действий не меняются». Для планов с > 64 вершин комнат в текущем + пространстве это не так: раньше resize посреди жеста предупреждал о невозможной позиции, теперь — + нет. +3. **Ни в одном комментарии issue владелец этот компромисс не видел и не утверждал** — я прочитал + всю переписку (`Аналитика`, `Вопросы для ТЗ`, `Решения владельца`, оба «Исправления по + CODE-REVIEW»): обсуждались Q1 (relevant HA update во время жеста) и Q2 (обычный hover), но не + порог 64 и не деление resize-preflight по размеру плана. + +Это Medium, не High: данные не портятся ни при каком сценарии (проверил код коммита, unconditional +fail-closed check на pointerup), поэтому это не блокирующий баг, а нераскрытое изменение поведения +вне заявленного скоупа задачи — ровно формулировка PROCESS.md «жёлтый вердикт допустим при +выполненных AC, если изменение ухудшает смежный сценарий». Находка в скоупе (введена этой же веткой +ради выполнения AC10) — чинится в этой же задаче, отдельный issue не заводится. + +**Что нужно от автора (не мой выбор, а перечисление опций для очередного цикла):** либо восстановить +живую точную проверку и для больших планов (тогда решать заново конфликт с AC10-бюджетом, из-за +которого её и убрали в r1), либо получить у владельца явное решение принять этот компромисс и +одновременно поправить формулировку changelog («без функциональных изменений» перестаёт быть верным +для больших планов), либо найти более дешёвый способ живой проверки (например, только у затронутой +комнаты вместо канонического union всего пространства — H1-r1 уже показал, что именно full clearance +был дорогим, а не сам факт проверки). + +## Что проверено и корректно + +- H1/H2/H3 из r2 закрыты — таблица выше, каждая проверена самостоятельно прогоном, а не с чужих слов. +- Регресс DOM `resize_pointer.unrelated_pointer_ignored`/`capture_loss_restores_dom` (пойман CI на + `a7bee0f5`, не был отдельной находкой r2) закрыт `046efe96`, подтверждено прогоном и CI. +- Commit-time geometry check (`_commitPhysicalGeometry` → `_checkSpacePhysicalGeometry`) остаётся + безусловным независимо от размера плана — прочитано построчно, не зависит от M1. +- Типизация (`fe03eae2`) семантически эквивалентна прежнему `any`-коду — прочитал diff + `junction-limits.ts` построчно (переход через промежуточный `completeGeometry` даёт тот же результат + для 'ok'/'degraded-extra'/'lightweight'/`null`/`undefined`, что и старое тройное сравнение). + `live-editor.ts` получил один дополнительный guard (`!!room.id &&`) — сужение, не расширение + поведения, риска регрессии нет. +- Новый unit-тест `resize-controller.test.mjs` («#451 live resize preflight is bounded by authored + contour complexity») проверяет именно границу (16×4=64 → true, 17×4=68 → false, один контур 65 → false) + — тест способен падать (проверил через чтение реализации: убери `<=` на `<`, и 64 упадёт). +- `no-new-any`, `check-docs`, `npm test`, `npm run build`+`bundle:sync` (сверка бандла) — зелёные + на точном HEAD, прогнано лично. +- Трейлеры `Issue: #451` — присутствуют во всех коммитах дельты. `User-Visible: no` для `046efe96` + и `444562e4` — точен (внутренний DOM-баг и обновление отпечатка, не новое поведение). Для `4b6b4abc` + трейлер тоже `User-Visible: no` — и вот это неточно ровно по причине M1. + +## Чего не проверял и почему + +- Полный `npm run golden:verify` — не гонял отдельно: дельта не меняет settled-состояние (эффект + M1 виден только во время активного resize-жеста, до commit/settled-кадра), а CI на HEAD показал + «Переиспользование: это дерево уже проверено» → golden job skipped легитимно (тот же tree-hash, + что уже проверялся 153/153 в r2). +- Полный `npm run benchmark:large-house-interaction` — не перегонял: диапазон дельты не меняет + веса editor-series (единственное затронутое ответвление — resize live-preflight, а он теперь + для фикстуры large-house **выключен** тем же порогом 64 < 80, то есть числа бюджета из r2 + (`editor median 484.7 ms ≤ 750`) по построению не изменятся от этой дельты — сам факт этого и есть + часть находки M1: бюджет проходит именно потому, что фикстура выше порога). +- Оставшиеся 18 «прямых совпадений» smoke-select (декор/opening-смоки на `_openingsR`/`NORM_W`) — + дельта в этих файлах чисто типовая (import type вместо value import, сигнатуры), не гонял отдельно; + 25 «слабых» совпадений на `_mode` не гонял — типовой рефакторинг чужого для них кода. +- `python -m pytest tests_backend` — диф не трогает `custom_components/**/*.py` (проверено чтением + `git diff --name-only`). +- `npm run invariants -- --config <...>` для конкретного конфига — не требуется отдельно (см. + «Унаследовано», дельта не меняет персистентную геометрическую форму). +- Мутация для `resizeLivePreflightAllowed` — не заводил: функция чистая, покрыта unit-тестом на все + три граничных случая, и мутационный тест не добавил бы нового при уже красном-способном юните. + +## Итог + +**Вердикт: жёлтый.** High: 0, Medium: 1 (M1, в скоупе — возвращается автору, отдельный issue не +заводится). H1–H3 из r2 закрыты корректно, дополнительный DOM-регресс закрыт корректно. Единственная +находка этого раунда — не баг в данных, а нераскрытое и не согласованное с ТЗ/changelog изменение +поведения resize-preflight для больших планов, появившееся как побочный эффект закрытия H3. + +--- + + + +## Материал раунда + +- Ветка: `issue/451-render-performance`, коммит `444562e47cb7` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `9489e0f2e24a6b4df6bd587703b2fa058ebb3a04` + ``` + git log --all --format='%H %T' | grep 9489e0f2e24a + ``` +- ТЗ `docs/specs/451-render-performance.md`, блоб `7c323a29110974aae369077214b9e2a74d9387c1` + ``` + git log --all --find-object=7c323a29110974aae369077214b9e2a74d9387c1 -- docs/specs/451-render-performance.md + ``` diff --git a/docs/reviews/CODE-REVIEW-451-r4.md b/docs/reviews/CODE-REVIEW-451-r4.md new file mode 100644 index 00000000..93caa5a8 --- /dev/null +++ b/docs/reviews/CODE-REVIEW-451-r4.md @@ -0,0 +1,284 @@ +# CODE-REVIEW #451 — заход r4 + +- **Issue:** #451 — «План тормозит: диагностика считается на каждый кадр, перетаскивание перерисовывает всё, нет фильтра обновлений» +- **Этап:** код-ревью (PROCESS.md §2.7) +- **Заход:** r4 · блокирующих циклов до этого раунда: 3/4 +- **Материал раунда:** ветка `issue/451-render-performance`, `HEAD = 07ba2ffbd1c02172d114ef27fb7cee2e40cf326e` + (сверено `git rev-parse HEAD` непосредственно перед выводом, дерево чистое, PROCESS.md §2.7/#312) +- **Дельта раунда:** `git diff 444562e47cb746dc1c7d740b66b2f832ca02f064..07ba2ffb` — SHA взят из машинного блока + «Материал раунда» документа `docs/reviews/CODE-REVIEW-451-r3.md` (дерево `9489e0f2e24a6…`, сверено + `git log --all --format='%H %T' | grep 9489e0f2e24a` — совпадает с `444562e4`). Файлы дельты + (без `dist/**` и копий бандла стенда): `src/houseplan-card.ts`, `src/houseplan-editor-runtime.ts`, + `src/resize-controller.ts`, новый `src/resize-live-preflight.ts`, `test/resize-controller.test.mjs`, + `demo/benchmark_large_house.mjs`, `demo/performance/card-contract.mjs`, `docs/images/screenshots.json`, + `docs/reviews/CODE-REVIEW-451-r3.md`. + +## Скоуп раунда + +r3 (жёлтый, High:0/Medium:1) закончился на `444562e4`. Единственная находка r3 — **M1**: фикс H3 из r2 +не просто вернул прежнее поведение, а ввёл порог `resizeLivePreflightAllowed(rooms, edgeBudget=64)` — +точная physical-geometry проверка во время resize-жеста была живой только для планов ≤ 64 вершин +комнат, для больших планов работала только на `pointerup`. Не заявлено в ТЗ, противоречило changelog +«внешний вид и результат действий не меняются». + +Автор ответил одним коммитом `5692a288` («fix: preserve live resize validation on large plans», +`User-Visible: no`): порог полностью удалён, вместо него — новый модуль `resize-live-preflight.ts` с +тремя функциями (`resizeLiveRoomIds`, `resizeLiveJunctionRoomIds`, `resizeLiveCandidateSpace`), которые +строят «локальный кандидат» — только затронутые комнаты плюс геометрически близкие к их границе +стены/сегменты/партиции/проёмы — и гоняют через него ту же самую `_checkSpacePhysicalGeometry`, +безусловно, на любом размере плана. `07ba2ffb` поверх — только обновление отпечатка скриншотов +документации (`docs: refresh screenshot fingerprint for #451`). + +Разбираю всю дельту по коду: это новый файл на 210 строк с нетривиальной геометрией (distance-to-segment, +segment-intersection, AABB-touch), напрямую заменяющий защитный механизм, который весь путь r2→r3→r4 и +был предметом ревью. + +## Как проверялось + +Дельта не трогает персистентный формат — только момент и объём вызова уже существующей проверки. +Валидация на точном HEAD (`07ba2ffb`) уже зелёная в CI: [Validate run 33927104551](https://github.com/Matysh/houseplan-card/actions/runs/33927104551), +success. Это покрывает `npx tsc --noEmit`, `npm test`, `npm run build`+сверку бандла и `check-docs` +(docs job зелёный на этом SHA) — **не перегонял эти четыре повторно**, см. правило дешёвых гейтов §8/§2.10. + +Что прогнал сам в этом раунде: + +| Команда | Результат | +|---|---| +| `npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs` | зелёный (нужно для собственных проб ниже) | +| `node scripts/no-new-any.mjs --base 444562e4 --head HEAD` | «Новых any нет» (225 строк в 3 файлах) | +| `npm run build && npm run bundle:sync` | зелёные, дерево бандла пересобрано без ошибок | +| `node scripts/smoke-select.mjs --base 444562e4 --head HEAD` | 19 прямых совпадений + 2 зарегистрированные связи, все по теме wall-union/junction/masonry/resize (список ниже) | +| `node demo/smoke_room_resize.mjs` | OK — реальные (не замоканные) сценарии `owner_boundary_clamped`, `corner_clamped`, `mixed_role_*` не задеты | +| `node demo/smoke_resize_pointer_real_plan.mjs` | OK | +| `node demo/smoke_junction_holes.mjs` | OK | +| `node demo/smoke_glow_fail_dark.mjs` | OK | +| `node demo/smoke_glow.mjs` | OK | +| `node demo/smoke_junction_patch_resilience.mjs` | OK | +| `node demo/smoke_multiwall_junction.mjs` | OK | +| `node demo/smoke_multiwall_strip_containment.mjs` | OK | +| `node demo/smoke_opening_measure.mjs` | OK | +| `node demo/smoke_optional_space_model.mjs` | OK | +| `node demo/smoke_wall_key_roundtrip.mjs` | OK | +| `node demo/smoke_wall_thickness_transition.mjs` | OK | +| `node demo/smoke_wall_union_isolation.mjs` | OK (включая генуинный, не замоканный `degradedPhysicalEditRejected` на **commit**-пути) | +| `node demo/smoke_zero_divider_taper.mjs` | OK | +| `node demo/smoke_real_plan_masonry.mjs` | OK («зарегистрированная связь» smoke-select — реальный план, разрывы кладки, которые синтетика не ловит) | +| `node demo/smoke_resize_wall_thickness.mjs` | OK («зарегистрированная связь») | + +Плюс собственная проба (не входит в существующий гейт, привожу как воспроизведение находки ниже): +прямой вызов `checkSpacePhysicalGeometry`/`resizeLiveCandidateSpace`/`resizeLiveJunctionRoomIds` из +`test-build/*.js` на реальном производственном фикстуре регрессии `test/fixtures/278-wall-union-isolation.json` +(#278) плюс синтетические «дальние» комнаты для имитации большого плана. + +## Закрытие r3 + +| Находка r3 | Чем закрыта | Где видно | +|---|---|---| +| **M1** (порог `edgeBudget=64`: точная live-проверка выключена целиком для планов > 64 вершин комнат, не заявлено в ТЗ/changelog) | Порог `resizeLivePreflightAllowed` удалён вовсе (`src/resize-controller.ts`, было 13 строк функции — теперь нет). Вместо него `resizeLiveCandidateSpace`/`resizeLiveRoomIds` (`src/resize-live-preflight.ts`) строят локальный физический кандидат и `_rszProjectPreview` гоняет `_checkSpacePhysicalGeometry` через него **безусловно**, на любом размере плана — деление по порогу исчезло как класс | `5692a2880f9ba645b9e2a897a828a9ea169a3a1a` (`src/houseplan-editor-runtime.ts:3654-3656`); сам прочитал — вызов больше не обёрнут в `if (resizeLivePreflightAllowed(...))`; `demo/benchmark_large_house.mjs` теперь структурно требует `resizeLivePreflightChecks >= 1` на `large-house` фикстуре (80 вершин, выше старого порога) — не даёт тихо вернуть скип | + +Формально M1 закрыта в буквальном прочтении (порог убран, вызов безусловный на любом размере). Но +дельта заменила один раскрытый компромисс на другой, нераскрытый — см. находку ниже: сам факт «вызов +происходит всегда» не означает «проверка обнаруживает то же самое, что обнаруживала до #451». + +## Находки + +### M1 (r4) — локальный кандидат для physical-geometry исключает соседнюю комнату, от которой зависит валидность стыка; живая проверка может пропустить реальный дефект кладки на любом размере плана (в скоупе, чинится в этой же задаче) + +**Файлы:** `src/resize-live-preflight.ts:59-65` (`resizeLiveRoomIds`), `:91-124` (`resizeLiveCandidateSpace`), +`src/houseplan-editor-runtime.ts:3654-3656` (`_rszProjectPreview`, использование). + +`resizeLiveCandidateSpace(sp, changedRoomIds)` строит кандидат для `_rszSpaceCandidateGeometry` (→ +`_checkSpacePhysicalGeometry`), отбирая **только** комнаты, чей id входит в `changedRoomIds` +(`resizeLiveRoomIds` — точное совпадение id, без расширения на соседей). Стены/сегменты/партиции +отбираются отдельно, по геометрической близости к границе **этих** комнат. Соседняя комната, которая +физически не изменилась в этом кадре (её id не в `changedRoomIds`), в кандидат не попадает вовсе — даже +если общий с ней узел/стена как раз и есть источник невалидности. + +Это не гипотетическое рассуждение о коде — воспроизвёл прогоном на реальном production-фикстуре +регрессии #278 (`test/fixtures/278-wall-union-isolation.json`, две комнаты `r1`/`r2`, общая стена +толщиной 20, историческая дефектная кладка, статус `degraded-extra`): + +```js +import { checkSpacePhysicalGeometry } from './test-build/plan-geometry-preflight.js'; +import { resizeLiveCandidateSpace } from './test-build/resize-live-preflight.js'; +// baseSpace = spaces[0] из test/fixtures/278-wall-union-isolation.json (r1, r2, стена 20см) +// + 80 «дальних» синтетических комнат (10..90 по x), чтобы получить план > 64 вершин. + +checkSpacePhysicalGeometry({ spaces: [largeSpace] }, largeSpace.id) +// -> { status: 'failed', reason: 'wall-degraded-extra', ok: false } ← ПОЛНОЕ пространство: дефект виден, +// независимо от того, сколько в плане посторонних комнат (80 или 0) + +const liveSpace = resizeLiveCandidateSpace(largeSpace, ['r1']); // r1 «изменилась», r2 — нет +liveSpace.rooms.map(r => r.id) // -> ['r1'] (r2 выброшена целиком) +checkSpacePhysicalGeometry({ spaces: [liveSpace] }, largeSpace.id) +// -> { status: 'ok', ok: true } ← ТОТ ЖЕ дефект больше не виден +``` + +Итог: во время resize-жеста, когда двигается только `r1`, а `r2` (владелец второй стороны той же +дефектной стены) не входит в `changedRoomIds`, живая проверка сообщает «геометрия в порядке» для +кандидата, который на самом деле нарушает контракт масонри — тот же самый контракт, ради которого +существует `_checkSpacePhysicalGeometry` и весь путь #278/H3(r2)/M1(r3). Это воспроизводится **на любом +размере плана** — добавление или удаление 80 посторонних комнат ничего не меняет, значит находка не +«ещё один порог», а более фундаментальная: набор комнат для physical-geometry кандидата определяется +по `changedRoomIds` (точное совпадение id), а не по геометрической смежности. + +Показательно, что для **другого** кандидата в том же методе — `_resizePreviewNodes` (junction-limit +проверка, #329) — автор уже использует `resizeLiveJunctionRoomIds` (один слой AABB-соседей), а не голый +`changedRoomIds`. Подставил тот же более широкий набор в physical-geometry кандидат — и дефект снова +виден: + +```js +const junctionIds = resizeLiveJunctionRoomIds(largeSpace.rooms, ['r1']); // -> ['r1', 'r2'] +const liveSpaceViaJunctionIds = resizeLiveCandidateSpace(largeSpace, junctionIds); +checkSpacePhysicalGeometry({ spaces: [liveSpaceViaJunctionIds] }, largeSpace.id) +// -> { status: 'failed', reason: 'wall-degraded-extra', ok: false } ← дефект снова обнаружен +``` + +Почему это Medium, а не техническая деталь: + +1. **Данные не портятся.** `_commitPhysicalGeometry` (не в этой дельте, прочитал — не изменился) вызывает + `_checkSpacePhysicalGeometry` на **полном** пространстве безусловно на `pointerup`. Невалидная + геометрия всё ещё не может сохраниться. +2. **Но живая обратная связь пользователю может отсутствовать именно там, где её восстановление и было + предметом M1(r3).** Пользователь дотягивает жест до конца, не видя «последней безопасной позиции», а + затем получает отказ commit (`resize.commit_failed`, класс регресса, который уже описан в H3 r2) без + предупреждения по пути — то есть худший, а не лучший исход по сравнению с раскрытым порогом r3: тот + хотя бы предсказуемо и одинаково выключал проверку выше 64 вершин; этот — непредсказуемо, в + зависимости от того, какая именно комната «официально изменилась» в данном кадре resize-солвера, + и на любом размере плана, включая маленькие. +3. **Ни ТЗ, ни changelog, ни коммит-сообщение** (`fix: preserve live resize validation on large plans`, + `User-Visible: no`) не упоминают эту границу — коммит заявляет ровно противоположное тому, что + происходит для дефектов, зависящих от соседней комнаты. +4. **Существующее покрытие не могло эту находку поймать.** Новые unit-тесты в + `test/resize-controller.test.mjs` проверяют только структуру фильтрации (какие id/стены попадают в + кандидат), не пропуская результат через `checkSpacePhysicalGeometry`. Новая проверка в + `demo/benchmark_large_house.mjs` (`deltas.resizeLivePreflightChecks < 1` роняет раннер) доказывает + только, что проверка **вызывается**, а не что она может вернуть `false` для реально невалидного + кандидата. `smoke_room_resize.mjs` (единственный реальный смок с геометрией без мока) использует + фикстуры, где невалидность — либо однокомнатная топология (`corner_clamped`), либо ровно совпадающая + с существующей стеной (`owner_boundary_clamped`, `wall-metadata`-путь, отдельная от physical-geometry + проверка, использует полный `sp.rooms` — не задета этой находкой); ни один существующий тест не + строит #278-подобный «дефект стыка, видимый только если обе стороны в модели». + +**Что нужно от автора (не мой выбор, перечисление опций для следующего цикла):** либо расширить набор +комнат physical-geometry кандидата тем же способом, что уже используется для junction-кандидата +(`resizeLiveJunctionRoomIds`, один слой AABB-соседей — проба выше показывает, что этого достаточно для +воспроизведённого случая; открытый вопрос — достаточно ли одного слоя для более сложных многосторонних +узлов, это стоит явного теста), либо обосновать и явно задокументировать в ТЗ/коде, почему набора +`changedRoomIds` достаточно для physical-geometry (если у меня неверна модель угрозы), и в любом случае +добавить тест, который прогоняет `resizeLiveCandidateSpace`-кандидат **через** `checkSpacePhysicalGeometry` +на заведомо дефектной (не замоканной) геометрии — по образцу пробы выше, лучше всего на самом фикстуре +#278, — чтобы AC «восстановлена live-валидация» имело названного свидетеля, который умеет краснеть. + +## Унаследовано из r3 (и через r3 из r1/r2), без повторной проверки + +Эта дельта не касается доказательной базы следующих пунктов — принимаю как есть: + +- H1–H3 из r2 (docs fingerprint, новые `any`, регресс `smoke_room_resize`) и дополнительный DOM-регресс + (`resize_pointer.unrelated_pointer_ignored`/`capture_loss_restores_dom`) — файлы их фиксов + (`houseplan-card.ts` типизация junction-limits, `live-editor.ts`, `046efe96` DOM-guard) не входят в + дельту `444562e4..HEAD`. Документ: `CODE-REVIEW-451-r2.md`/`-r3.md`, SHA `cb68492c`/`444562e4`. +- AC1–AC2, AC7 (разделение intake/визуальной инвалидизации, dependency projection, last-wins HA во время + жеста) — файлы фильтра `hass`/dependency classifier не в дельте `444562e4..HEAD`. Документ: + `CODE-REVIEW-451-r2.md`, SHA `cb68492c`. +- AC4 (diagnostics cache) — не тронут этой дельтой. +- AC9 (golden/canonical screenshots) — единственный тронутый в этой дельте артефакт — + `docs/images/screenshots.json` (отпечаток источника, коммит `07ba2ffb`); Linux-съёмка на предыдущем + SHA (`444562e4`) была канонической (r3: [run 33926924502]) и её результат (10/10 без диффов) не + меняется этой дельтой — сама дельта не трогает `src/**` рендер-путь, только момент вызова физической + проверки во время resize, до commit/settled-кадра. Отдельно не перепрогонял golden — не требуется: + результирующий кадр после `pointerup` идентичен (полная проверка на commit не изменилась). +- Инварианты модели по всем моделям проекта — часть `npm test` (зелёный на HEAD в CI); конфиг-специфичная + команда `npm run invariants -- --config …` не нужна отдельно: дельта не меняет персистентную форму + (`walls[]`, `wall_segments`, `marker.space`, `open_spans`) — только состав кандидата, временно + собираемого в памяти для live-проверки во время жеста, никогда не записываемого в конфиг. +- `INITIAL_VIEW_GZIP_CEILING` 297000→298000 и его обоснование — не тронуты этой дельтой. + +## Что проверено и корректно + +- Порог `resizeLivePreflightAllowed`/`edgeBudget=64` из r3 действительно удалён целиком — прочитал diff + `resize-controller.ts`, функции больше нет; `_rszProjectPreview` больше не содержит ветвления по + размеру плана для physical-geometry вызова. +- 17 из 21 отмеченных `smoke-select` тестов (19 прямых + 2 зарегистрированных за вычетом 6 слабых + `cellCm`-совпадений, см. «Чего не проверял») прогнаны лично, все зелёные — включая генуинный (не + замоканный) commit-time `degradedPhysicalEditRejected` в `smoke_wall_union_isolation.mjs` и реальный + план в `smoke_real_plan_masonry.mjs`. +- Реальные (не замоканные) мелкоплановые сценарии `smoke_room_resize.mjs` (`owner_boundary_clamped`, + `corner_clamped`, `mixed_role_*`) не регрессировали при переходе на безусловный локальный кандидат — + прогнал, зелёные. +- `_commitPhysicalGeometry` (полное пространство, безусловный вызов на `pointerup`) не тронут этой + дельтой — прочитал, данные при коммите остаются fail-closed независимо от находки M1(r4): невалидная + геометрия не может сохраниться, регресс только в live-обратной связи посреди жеста. +- Новые unit-тесты `resize-controller.test.mjs` корректно проверяют то, что они заявляют проверять + (структура фильтрации id/стен/партиций/проёмов на 100-комнатном синтетическом плане) — прочитал, тест + умеет падать на этом узком контракте (например, `resizeLiveRoomIds` вернёт лишний id, если убрать + фильтр по `changed`). Находка M1(r4) не в том, что эти тесты неверны, а в том, что они не покрывают + промежуточный конечный результат (`checkSpacePhysicalGeometry` на построенном кандидате). +- `no-new-any --base 444562e4 --head HEAD` — 0 новых `any` в 225 добавленных строках 3 файлов. +- Новая инструментация `demo/benchmark_large_house.mjs` (`physicalPreflightCount`/`physicalPreflightMs`, + ассерт `resizeLivePreflightChecks < 1`) корректно доказывает то немногое, что доказывает: вызов + происходит хотя бы раз во время editor-резайза на large-house фикстуре — не более. +- Трейлеры `Issue: #451` присутствуют, `User-Visible: no` для `5692a288` и `07ba2ffb` — точны для + заявленного эффекта (реализация не меняет видимое поведение, когда проверка срабатывает), но не + раскрывают найденную M1(r4) границу, где она не срабатывает. + +## Чего не проверял и почему + +- Полный `npm run golden:verify`, `npx tsc --noEmit`, `npm test`, `npm run build`+сверка бандла отдельно + от CI — не перегонял: Validate зелёный на точном HEAD `07ba2ffb` + ([run 33927104551](https://github.com/Matysh/houseplan-card/actions/runs/33927104551)), дешёвые гейты + §8 сошлись на этом прогоне. +- 6 слабых `smoke-select`-совпадений по единственному общему идентификатору `cellCm` + (`smoke_backdrop_guard`, `smoke_danger_confirmation`, `smoke_decor`, `smoke_grid_scale_invariance`, + `smoke_help_affordance`, `smoke_space_scale_defaults`) — не гонял: `cellCm` — параметр с fallback по + умолчанию в новом файле, общий для всей кодовой базы идентификатор без содержательной связи с темой + этих смоков (ни один не про resize/physical-geometry); риск по существу покрыт целевыми + wall-union/junction/resize смоками выше. +- Полный `npm run benchmark:large-house-interaction` — не перегонял сам; автор привёл 7 прогонов с + зелёными абсолютными бюджетами на этом SHA. Дельта этого раунда не меняет веса editor-series (то же + число вызовов physical-geometry на move, что и раньше, только на другом наборе комнат) — по построению + не должна была измениться, и находка M1(r4) не является перформанс-регрессией (наоборот: локальный + кандидат обычно дешевле полного пространства). +- `python -m pytest tests_backend` — диф не трогает `custom_components/**/*.py` (проверено + `git diff --name-only 444562e4..HEAD`). +- Мутация для `resizeLiveCandidateSpace`/`resizeLiveRoomIds` через `scripts/mutation-gate.mjs` — не + заводил (это не моя роль); вместо этого привёл воспроизводимую пробу через прямой вызов + скомпилированных пары чистых функций на production-фикстуре #278 — она и есть демонстрация «чем + краснеет» для находки M1(r4). Постоянный мутант в гейте — тоже часть того, что нужно от автора при + закрытии находки. +- Полная browser-smoke матрица (222 файла) — не прогонял; это предрелизный гейт (PROCESS.md §8), дельта + локальна (4 файла кода) и `smoke-select` по точному диапазону раунда покрыл релевантную тему. + +## Итог + +**Вердикт: жёлтый.** High: 0, Medium: 1 (M1(r4), в скоупе — возвращается автору, отдельный issue не +заводится). Порог из M1(r3) действительно удалён, но замена (`resizeLiveCandidateSpace` с фильтрацией +комнат по точному `changedRoomIds`) вводит новый, более скрытый пробел того же класса: воспроизведён +прогоном на реальном production-фикстуре #278, что живая physical-geometry проверка может вернуть `ok` +для кандидата, эквивалентного заведомо дефектной (`wall-degraded-extra`) полной геометрии, если +источник дефекта — стык с соседней, формально «неизменившейся» комнатой. Данные не портятся +(`_commitPhysicalGeometry` на `pointerup` не изменился и остаётся безусловным), поэтому находка не +блокирует как High, но AC «restore live resize validation» в исходном смысле (было в r1/r2 до #451 +сломавших это) не является полностью восстановленным для этого класса дефектов ни на одном размере +плана — направление минимального фикса (переиспользовать уже существующий `resizeLiveJunctionRoomIds`) +подтверждено той же пробой. + +--- + + + +--- + + + +## Материал раунда + +- Ветка: `issue/451-render-performance`, коммит `07ba2ffbd1c0` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `c2def02d1a1409564ed4eaa3661b663cbba356c1` + ``` + git log --all --format='%H %T' | grep c2def02d1a14 + ``` +- ТЗ `docs/specs/451-render-performance.md`, блоб `7c323a29110974aae369077214b9e2a74d9387c1` + ``` + git log --all --find-object=7c323a29110974aae369077214b9e2a74d9387c1 -- docs/specs/451-render-performance.md + ``` diff --git a/docs/reviews/SPEC-REVIEW-451-r1.md b/docs/reviews/SPEC-REVIEW-451-r1.md new file mode 100644 index 00000000..8b33a778 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-451-r1.md @@ -0,0 +1,226 @@ +# SPEC-REVIEW-451-r1 + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/451 +- **Этап:** spec (PROCESS.md §2.4) +- **Заход:** r1 (первое ревью ТЗ, лёгкий трек не применяется — S2-analysis назвал + четыре нарушенных критерия §5: риск 9/10, больше одной поверхности, влияние + на performance, влияние на touch) +- **Материал:** `docs/specs/451-render-performance.md`, коммит `71c3360363991a042fbf3d84a6aeb4ecb772c83f` + (`git rev-parse HEAD` на момент вывода), ветка `issue/451-render-performance` + (HEAD detached at `origin/issue/451-render-performance`) +- **Вердикт:** жёлтый · заход r1 · блокирующих циклов 1/4 · High: 0 · Medium: 3 → в задаче + +## Скоуп ревью + +Проверялся только сам файл ТЗ и тело/комментарии issue #451 — продуктового кода +для #451 ещё нет (коммит `71c33603` содержит только `docs/specs/451-render-performance.md` +и запись в `docs/specs/README.md`). Ревью состязательное: разбор велся без +устных пояснений автора, по тексту ТЗ и по текущему `dev`. + +## Как проверялось + +1. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` целиком (включая §2.4, + §2.10, §4, §5, §7.1, §7.2, §8). +2. Прочитано тело issue #451 и все комментарии (замеры, причины A/B/C/D, + аналитика, вопросы Q1/Q2 и решения владельца). +3. Прочитан ТЗ-документ целиком (`docs/specs/451-render-performance.md`, 516 строк). +4. Факты, заявленные в §3 ТЗ как установленные причины, сверены с текущим + `dev` построчно: + - `_renderBody()` безусловно вызывает `houseplanDiagnostics()` — + `src/houseplan-card.ts:11315`, подтверждено; + - обход всех живых bindings в `houseplanDiagnostics()`/`_bindingStatus()` — + `src/houseplan-card.ts:4748-4771`, подтверждено; + - три `data-*` атрибута читают один и тот же объект `diagnostics`, + вычисленный один раз на рендер (строки `11349-11351` и `11430-11432` + используют одну и ту же переменную `diagnostics`) — «одно число, один + источник» соблюдено уже в текущем коде и не нарушается контрактом ТЗ; + - `_view`/`_zoom` зарегистрированы `state: true` — `src/houseplan-card.ts:2557-2558`, + подтверждено; + - `shouldUpdate()` в файле отсутствует (есть только `willUpdate()`) — + подтверждено; + - `createRenderDeviceSnapshot()` существует и используется — `src/houseplan-card.ts:4687`, + подтверждено. +5. Сверены абсолютные потолки, упомянутые в §11.3 ТЗ, с `demo/performance/budgets.json`: + `panZoomMs.hardMaxMs = 500`, `stateUpdateMs.hardMaxMs = 1000` — совпадает + с тем, что ТЗ называет уже существующим. +6. Проверено существование каждого упомянутого в ТЗ файла-адресата документации: + `docs/PERFORMANCE.md`, `docs/SCREENSHOTS.md`, `demo/performance/README.md`, + `demo/benchmark_large_house.mjs`, `demo/performance/card-contract.mjs` — + через `find`/`ls` по репозиторию. +7. Проверены связанные issue (#34, #82, #137, #156, #380, #396, #449) через + `gh issue view` — все существуют и релевантны тому, для чего их приводит ТЗ. +8. Проверен термин `POINTER_HOVER_QUERY`/«fine hover capability» из §9 ТЗ — + совпадает с реальной константой `src/pointer-modality.ts:5`. +9. Численные данные §11.3 («наблюдавшихся 3 432–5 538 мс») сверены с телом + issue и всеми его комментариями (`gh issue view --json body,comments`) — + `grep` на «5538»/«5 538» не дал ни одного совпадения ни в issue, ни где-либо + в репозитории вне самого нового ТЗ. + +Гейты (`typecheck`/`test`/`build`/смоки/perf) не прогонялись и не должны были: +на этом этапе нет продуктового кода, диапазон изменений — только `docs/specs/**`. +Это осознанное решение по этапу, а не пропуск. + +## Что проверено и корректно + +- Обязательные разделы §7.1 присутствуют и в правильном порядке: сценарий (§1), + что человек увидит до/после (§2), проблема (§3), скоуп/не-скоуп (§4-5), + контракт поведения (§6-8), UX/touch/a11y (§9), модель данных/совместимость/ + безопасность (§10), performance (§11), AC1-AC13 с методом доказательства + (§12), план автотестов (§13), план реализации (§14), release-артефакты (§15), + риски (§16), откат (§17), явный блок «принято предположительно» (§18). +- Продуктовые решения владельца (Q1 — deferred-last-wins после `pointerup`/ + `pointercancel`; Q2 — обычный hover входит в задачу через lightweight-слой) + корректно перенесены в контракт (§6.3, §8.3) и не переоткрываются как + вопросы — ТЗ их фиксирует как решённые, а не как предположения автора. +- Диагноз причин A/B/C в §3 не является догадкой: каждое утверждение проверяется + чтением текущего кода (см. «Как проверялось», п.4) и совпадает с ним дословно. +- Собственное наблюдение автора из комментария issue («редакторские жесты не + замерялись, синтетический `pointermove` не доходит до обработчиков») корректно + закрыто требованием в плане тестов: «Synthetic events обязаны доходить до + production handlers; `0` событий у handler не принимается как доказательство + `0` renders» (§13.2). Это именно то усиление, которого просил сам owner-комментарий + («жест не перерисовывает содержимое» проверяется на демо-стенде, где портить + нечего). +- Non-scope (§5) точно очерчивает границу: внешний вид, hit area, snap + tolerance, geometry-алгоритмы, persisted config/schema, ослабление бюджетов — + всё явно исключено, что снижает риск скрытого расширения скоупа при + реализации сложной (9/10) задачи. +- AC1-AC13 пронумерованы, у каждого указан метод доказательства + (`unit`/`smoke`/`golden`/`performance`/«код-ревью»/«review»), формулировки + проверяемы по структурным assertions §11.2 (счётчики full render, а не + субъективные «стало быстрее»). +- «Один жест — один результат» выдержан симметрично для всех трёх редакторов + через таблицу §8.4, без привилегирования Плана перед Устройствами/Подложкой. +- Откат (§17) реалистичен: revert коммитов, точечный аварийный fallback на + один жест без отключения diagnostics cache/HA filter, с обязательным issue — + это не пустая формальность, а конкретный механизм. +- Ссылки на связанные issue (#34, #82, #137, #156, #380, #396, #449) все + существуют и действительно относятся к тем контрактам, которые ТЗ обязуется + не сломать (проверено `gh issue view`). + +## Находки + +### M1 — `docs/PERFORMANCE.md` не существует, но назван обязательным артефактом (Medium, в скоупе) + +**Файл:** `docs/specs/451-render-performance.md`, строки 13, 390-391 (AC13), 452 +(план реализации, п.7), 469 (release-артефакты). + +**Проблема:** ТЗ трижды в разных разделах требует обновить `docs/PERFORMANCE.md` +как уже существующий канонический документ performance harness («**Связано:** +… `docs/PERFORMANCE.md`»; AC13 «`docs/PERFORMANCE.md` и `demo/performance/README.md` +описывают новый профиль…»; §15 «Также обновляются: `docs/PERFORMANCE.md`»). +Такого файла в репозитории нет ни на `dev`, ни где-либо ещё — проверено +`find docs -iname "*performance*"`. Канонический документ performance harness +на сегодня — `demo/performance/README.md` (на него явно ссылается +`docs/DEVELOPMENT.md:165`). + +**Как проявится:** разработчик, следуя ТЗ буквально, либо не найдёт файл и +пропустит часть AC13 «не сделано» под видом выполненного, либо создаст новый +`docs/PERFORMANCE.md` параллельно уже существующему `demo/performance/README.md`, +получив два источника документации об одном и том же harness — ровно тот +класс дублирования, который процесс просит избегать («параллельных бэклоги/ +документы» и принцип «одно число/один источник» по духу, здесь — один +документ на один факт). + +**Требуется:** исправить ссылку на `demo/performance/README.md` (или явно +решить создать новый `docs/PERFORMANCE.md` как отдельный AC — тогда это +техническое решение места документации, которое должно быть либо снято, либо +явно помечено как «новый файл» в §18/§13.3, а не как правка существующего). + +### M2 — `docs/SCREENSHOTS.md` не существует (Medium, в скоупе) + +**Файл:** `docs/specs/451-render-performance.md`, строка 428 (§13.3). + +**Проблема:** «после изменения `src/**` — canonical screenshots по +`docs/SCREENSHOTS.md`, inspection и существующие golden» — файла +`docs/SCREENSHOTS.md` в репозитории нет (`find docs -iname "SCREENSHOTS.md"` +не находит ничего). Термин «canonical screenshots» в проекте реален (встречается +в сообщениях коммитов и предыдущих код-ревью, например +`docs/reviews/CODE-REVIEW-126-r1.md:6`), но процесс их пересъёмки и приёмки +описан в `PROCESS.md` §8 (`npm run build && node demo/docs/capture.mjs`, +`npm run docs:accept -- --reviewed --from=<артефакт>`) и в `scripts/check-docs.mjs`, +а не в файле с таким названием. + +**Как проявится:** тот же риск, что в M1 — ссылка на несуществующий файл в +плане автотестов не даёт разработчику конкретного места, которое нужно +обновить, и не является проверяемым шагом «как есть». + +**Требуется:** заменить ссылку на фактический процесс (`PROCESS.md` §8 / +`scripts/check-docs.mjs` / `demo/docs/capture.mjs`) или на реальный файл, если +он появится к моменту реализации. + +### M3 — верхняя граница диапазона «наблюдавшихся» замеров в §11.3 не подтверждена ни одним источником (Medium, в скоупе) + +**Файл:** `docs/specs/451-render-performance.md`, строки 329-332 (§11.3). + +**Проблема:** обоснование bootstrap `hardMaxMs` в таблице §11.3 звучит так: +«…полученные как безопасный порядок ниже наблюдавшихся **3 432–5 538 мс** +полного-render времени…». Число `3 432 мс` действительно есть в issue +(«Перетаскивание: суммарно в обновлениях | 3 432 мс»). Число `5 538 мс` +не встречается ни в теле issue, ни в одном из шести комментариев +(`gh issue view 451 --json body,comments`, поиск `5538`/`5 538` — ноль +совпадений), ни где-либо в репозитории вне самого этого ТЗ. + +**Как проявится:** конкретные абсолютные потолки (`500/500/500/750/250 мс`, +`maxSingleLongTaskMs<=150`, `longTaskTotalMs<=300`) станут блокирующим CI-гейтом +(«fail the runner независимо от timing result», §11.2). Если верхняя граница +диапазона, из которого эти потолки якобы выведены «безопасным порядком», +не подкреплена измерением, у порогов нет прослеживаемой доказательной базы — +именно то «утверждение, которого нет ни в одном документе и не помечено как +предположение», которое ревью обязано ловить отдельно от продуктовых догадок. +Не исключено, что число реально измерено автором (в issue упомянуто отдельное +инструментирование «снято со страницы»), но в тексте ТЗ это не названо явно как +несохранённое сырое измерение, а подано как факт наравне с процитированным +`3 432 мс`. + +**Требуется:** либо назвать источник `5 538 мс` (например, «долгие задачи — +30 шт/3 298 мс плюс базовая стоимость рендера» с явным выводом формулы), либо +убрать конкретное число и явно пометить диапазон как «оценка автора спецификации, +поменять свободно» в §18, либо пересчитать от фактически процитированных в issue +цифр. + +## Что не проверялось и почему + +- **Продуктовый код** — не прогонялись `npx tsc --noEmit`, `npm test`, `npm run + build`, смоки, golden, invariants, performance-профили. Причина: диапазон + этого коммита — только `docs/specs/**`, продуктового кода для #451 ещё нет; + это гейты этапа `code`, а не `spec`. +- **Полнота dependency-списка §6.2** (все ли реальные top-level `hass.*` поля, + которые сейчас читает `houseplan-card.ts`, туда попадут) — не проверялась + построчным аудитом всех обращений к `this.hass.*`: §18 прямо оставляет точный + состав dependency projection «принято предположительно, поменять свободно» + и делегирует полноту code review реализации (AC2 доказывается `unit` + `targeted + smoke`, а не спек-ревью). +- **Достижимость абсолютных бюджетов §11.3 в реальном рантайме** — это будет + доказано конкретным CI-прогоном нового профиля при реализации (AC10), спек-ревью + проверяет только прослеживаемость происхождения чисел (см. M3), а не их + реалистичность. +- **Полная матрица редакторских жестов** (Plan/Devices/Decor continuous + interactions за пределами трёх «representative editor series») — ТЗ сознательно + переносит её в deterministic smoke, а не в performance-профиль (§18), это + решается на этапе реализации и проверяется в code review по факту + написанных смоков. + +## Материал раунда + +- Ветка: `issue/451-render-performance` +- HEAD на момент вывода: `71c3360363991a042fbf3d84a6aeb4ecb772c83f` +- Дерево материала: `docs/specs/451-render-performance.md` (единственный + затронутый ТЗ-файл), `docs/specs/README.md` (индексная строка) +- Команда поиска дерева: `git log --all --format='%H %T' | grep <дерево HEAD>` + +--- + + + +## Материал раунда + +- Ветка: `issue/451-render-performance`, коммит `71c336036399` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `c395260a0f49bc3004d1cb443cae97c4f9b88001` + ``` + git log --all --format='%H %T' | grep c395260a0f49 + ``` +- ТЗ `docs/specs/451-render-performance.md`, блоб `d9c4dfdc78feeeecfaf1ec41bdf665f22d21944e` + ``` + git log --all --find-object=d9c4dfdc78feeeecfaf1ec41bdf665f22d21944e -- docs/specs/451-render-performance.md + ``` diff --git a/docs/reviews/SPEC-REVIEW-451-r2.md b/docs/reviews/SPEC-REVIEW-451-r2.md new file mode 100644 index 00000000..fe1f2d14 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-451-r2.md @@ -0,0 +1,149 @@ +# SPEC-REVIEW-451-r2 + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/451 +- **Этап:** spec (PROCESS.md §2.4) +- **Заход:** r2 · блокирующих циклов израсходовано 1 из 4 (зелёный вердикт бюджет + не тратит, #227) +- **Материал:** `docs/specs/451-render-performance.md`, коммит `2bf65d8bc894c223a3fd3098cac66d0af22fcfcb` + (`git rev-parse HEAD` на момент вывода), ветка `issue/451-render-performance` +- **Вердикт:** зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0 + +## Скоуп ревью r2 (по дельте, PROCESS.md §2.10) + +Предыдущий раунд — `docs/reviews/SPEC-REVIEW-451-r1.md`, вердикт жёлтый, High 0 / +Medium 3, материал зафиксирован на коммите `71c3360363991a042fbf3d84a6aeb4ecb772c83f` +(SHA резолвится и сейчас, это прямой предок текущего HEAD — `git merge-base +--is-ancestor 71c33603 HEAD` истинно, находка «мёртвый SHA» не применима). + +Дельта раунда — `git diff 71c33603..2bf65d8b -- docs/specs/451-render-performance.md`: +7 хунков, все внутри одного файла ТЗ, ни один не расширяет скоуп и не меняет +контракт поведения (Q1/Q2, AC1-AC12, §6-9 не тронуты). Автор сам объявил это +как «правки по SPEC-REVIEW r1» одним комментарием, перечислив M1/M2/M3 — дельта +подтверждена построчно и совпадает с заявленным без остатка. + +Разбор в r2 сокращён до дельты: заново проверены только участки текста, которые +правка задевает (M1/M2/M3 и их непосредственный контекст — §0 «Связано», §11.3, +AC13, §13.3, §14 п.7, §15). Продуктовая рамка (§1 сценарий, §2 что видит +человек, §7.1 обязательные разделы, AC1-AC12, §18 «принято предположительно») +не переоткрывалась — основание в разделе «Унаследовано из r1» ниже. + +## Как проверялось + +1. Прочитан текст всех трёх находок r1 и комментарий автора «Исправления по + SPEC-REVIEW r1» в issue #451. +2. Получен точный дифф правки: `git diff 71c3360363991a042fbf3d84a6aeb4ecb772c83f..2bf65d8bc894c223a3fd3098cac66d0af22fcfcb -- docs/specs/451-render-performance.md`. +3. Для каждой из M1/M2/M3 подтверждено построчно, что упомянутая проблемная + строка либо удалена, либо заменена корректной ссылкой (см. таблицу ниже). +4. Проверено отсутствие остаточных упоминаний удалённых сущностей во всём + файле: `grep -n "PERFORMANCE.md\|SCREENSHOTS.md\|5 538\|5538" + docs/specs/451-render-performance.md` — ноль совпадений вне уже + рассмотренных строк §11.3 (сами числа `3 432`/`98,9`/`4,2` присутствуют + намеренно, как новое обоснование). +5. Новое обоснование `hardMaxMs` в §11.3 сверено с телом issue #451: + `3 432 мс` — итоговая сумма из строки «Перетаскивание: суммарно в + обновлениях» таблицы A/B-эксперимента; `98,9 мс` — среднее обновление + сценария «Наведение мыши (5 с)» из исходной таблицы замеров. Оба числа + реальны и присутствуют в issue дословно (сверено `gh issue view 451 + --json body,comments`), в отличие от снятого `5 538 мс`, для которого r1 + не нашёл источника. +6. Арифметика новой фразы «500 мс на 120 moves… не более 4,2 мс на событие» + проверена: 500/120 = 4,1(6) — округление в сторону ceiling («не более 4,2») + математически корректно как верхняя граница, хоть и не самое плотное число; + это не искажает вывод и не является новой непрослеживаемой догадкой. +7. Проверено, что коммит `2bf65d8b` не тронул ничего, кроме + `docs/specs/451-render-performance.md` (`git show --stat 2bf65d8b`), и несёт + корректные трейлеры `Issue: #451` / `User-Visible: no` — правка ТЗ не + является видимым пользователю поведением, `no` уместен. +8. Дважды сверено, что дельта локальна и не подпадает ни под одно условие + «разбор остаётся полным» из §2.10: `dev` не ушёл вперёд (задача ветвится от + него же, ребейза не было), контракт поведения не менялся, новая подсистема + не затронута, объём дельты (12 строк добавлено/удалено в одном файле) на + порядки меньше исходного ТЗ (515 строк). + +Гейты (`typecheck`/`test`/`build`/смоки/perf) не прогонялись — на этапе `spec` +продуктового кода нет, диапазон изменений всей задачи по-прежнему только +`docs/specs/**` и `docs/reviews/**`. Это то же осознанное решение, что и в r1, +не пропуск. + +## Закрытие раунда r1 + +| Находка | Чем закрыта | Где это видно | +|---|---|---| +| **M1** — `docs/PERFORMANCE.md` назван обязательным артефактом, хотя не существует | Все три упоминания удалены, единственным каноном оставлен существующий `demo/performance/README.md` | `docs/specs/451-render-performance.md:13` (Связано), AC13 (строка 392), §14 п.7 (строка 455), §15 (строка 472) — `docs/PERFORMANCE.md` отсутствует во всём файле (`grep` ноль совпадений) | +| **M2** — `docs/SCREENSHOTS.md` в §13.3 не существует | Ссылка заменена реальным процессом: `PROCESS.md` §8, `demo/docs/capture.mjs`, `npm run docs:accept` | `docs/specs/451-render-performance.md:430-431` | +| **M3** — верхняя граница «5 538 мс» в §11.3 не подтверждена ни одним источником | Число снято; обоснование `hardMaxMs` теперь опирается только на дважды процитированные в issue числа — `3 432 мс` (pan, суммарно) и `98,9 мс` (hover, среднее), с явным выводом целевого значения 4,2 мс/событие | `docs/specs/451-render-performance.md:329-334`; оба числа найдены в issue #451 (`gh issue view --json body,comments`) | + +Все три находки закрыты правкой текста, а не заявлением автора — проверено +чтением итогового файла, не с чужих слов. + +## Унаследовано из r1 + +Без повторной проверки в r2 принято всё, чего дельта не касается — документ +`docs/reviews/SPEC-REVIEW-451-r1.md`, материал `71c3360363991a042fbf3d84a6aeb4ecb772c83f` +(SHA живой, подтверждён `git merge-base --is-ancestor` выше): + +- обязательные разделы §7.1 присутствуют и в правильном порядке (сценарий, + что видит человек, проблема, скоуп/не-скоуп, контракт, UX/touch/a11y, модель + данных, performance, AC1-AC13, план тестов, план реализации, release- + артефакты, риски, откат, §18); +- продуктовые решения владельца Q1 (deferred-last-wins после + `pointerup`/`pointercancel`) и Q2 (обычный hover в скоупе, лёгкий слой) + корректно перенесены в контракт §6.3/§8.3 и не переоткрываются как вопросы; +- диагноз причин A/B/C в §3 построчно сверен с `dev` в r1 и не является + догадкой (диагностика в `_renderBody`, `_view`/`_zoom` как `state: true`, + отсутствие `shouldUpdate`, единый источник трёх `data-*` атрибутов); +- non-scope (§5) очерчивает границу без расширения при реализации; +- AC1-AC13 пронумерованы, метод доказательства указан для каждого, проверяемы + структурными assertions §11.2, а не субъективными оценками; +- «один жест — один результат» симметричен для трёх редакторов через §8.4; +- откат (§17) содержит конкретный механизм, а не формальность; +- ссылки на связанные issue (#34, #82, #137, #156, #380, #396, #449) + существуют и релевантны. + +Дельта r2 ни одного из этих пунктов не задевает: правки лежат только в трёх +изолированных фрагментах (ссылки на несуществующие файлы и одно число), ни один +AC, продуктовое решение или обязательный раздел не переписаны. + +## Находки + +Нет. High: 0. Medium: 0. Новых догадок, выданных за факт, дельта не внесла — +оба новых числа (`3 432`, `98,9`) прослеживаются к issue дословно. + +## Что не проверялось и почему + +- **Продуктовый код** — по-прежнему не существует для #451; гейты `spec` этого + не требуют. +- **Полнота dependency-списка §6.2, реалистичность бюджетов §11.3 в рантайме, + полная матрица редакторских жестов** — те же три пункта, что и в r1, + делегированы на код-ревью (§18 ТЗ явно называет их «принято предположительно» + либо доказываемыми `unit`/`performance` на этапе реализации); дельта r2 их не + меняла и не должна была. +- **Арифметическая точность округления 4,1(6)→4,2** проверена вручную (см. п.6 + выше), отдельного гейта для этого нет и не требуется — это текстовое + обоснование, а не защитный AC. + +## Материал раунда + +- Ветка: `issue/451-render-performance` +- HEAD на момент вывода: `2bf65d8bc894c223a3fd3098cac66d0af22fcfcb` +- Предыдущий SHA (r1): `71c3360363991a042fbf3d84a6aeb4ecb772c83f` — живой, + прямой предок текущего HEAD +- Дерево материала: `docs/specs/451-render-performance.md` (единственный + изменённый в дельте файл) +- Команда поиска дельты: `git diff 71c3360363991a042fbf3d84a6aeb4ecb772c83f..2bf65d8bc894c223a3fd3098cac66d0af22fcfcb -- docs/specs/451-render-performance.md` + +--- + + + +## Материал раунда + +- Ветка: `issue/451-render-performance`, коммит `2bf65d8bc894` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `125aadd5cb53e6af3ffd14998c20855dbfd2d503` + ``` + git log --all --format='%H %T' | grep 125aadd5cb53 + ``` +- ТЗ `docs/specs/451-render-performance.md`, блоб `7c323a29110974aae369077214b9e2a74d9387c1` + ``` + git log --all --find-object=7c323a29110974aae369077214b9e2a74d9387c1 -- docs/specs/451-render-performance.md + ``` diff --git a/docs/specs/451-render-performance.md b/docs/specs/451-render-performance.md new file mode 100644 index 00000000..7c323a29 --- /dev/null +++ b/docs/specs/451-render-performance.md @@ -0,0 +1,517 @@ +# ТЗ #451 — фильтрация render и лёгкий live-слой взаимодействий + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/451 +- **Редакция:** первая редакция для независимого ревью; статус определяется только метками issue +- **Тип / приоритет:** bug / P1 +- **Оценка:** пользовательская ценность 10/10; ценность для разработки 10/10; + сложность и риск 9/10 +- **Область:** общий lifecycle `houseplan-card`, HA render snapshot и registry, + View, редакторы Плана/Устройств/Подложки, камера и continuous interactions, + diagnostics, performance harness +- **Модель данных:** без новых полей, миграции и backend-изменений +- **Связано:** #34, #82, #137, #156, #380, #396, #449, + `demo/performance/README.md` + +## 1. Сценарий + +**Персона:** администратор дома, который использует средний или большой план в +Home Assistant на desktop, планшете или настенной панели. + +**Поверхности и момент:** обычный View и все три редактора во время движения +мыши, pan/pinch/zoom, рисования, размещения, перетаскивания, вращения или +resize; также обычная работа карточки, когда HA присылает фоновые state ticks. + +На измеренном плане с 5 пространствами, 139 маркерами и 144 устройствами один +полный update занимает десятки миллисекунд. Hover запускает его почти на каждое +движение указателя, pan дал 76 полных updates за четыре протяжки, а нерелевантные +HA ticks продолжают делать ту же работу в покое. Пользователь видит рывки и +замирания вплоть до 451 мс, хотя неподвижная уже нарисованная сцена сама по себе +не требует постоянной работы. + +## 2. Что человек увидит до и после + +**До:** план отстаёт от указателя, hover и редакторские preview дёргаются, а +фоновые изменения посторонних HA entities могут вызвать заметный фриз. + +**После:** тот же план, те же эффекты и те же результаты действий двигаются +плавно; HA-изменения, относящиеся к плану, остаются актуальными, а посторонние +изменения не заставляют карточку заново строить всю сцену. + +Преднамеренных визуальных изменений нет. После завершения любого жеста +канонический итог и итоговый кадр совпадают с текущим поведением. + +## 3. Проблема и установленные причины + +### 3.1 Диагностика находится в горячем render-path + +`_renderBody()` безусловно вызывает `houseplanDiagnostics()`. Метод снова +обходит все сохранённые HA-привязки и вызывает `_bindingStatus()` для каждой, +хотя render использует результат лишь в трёх диагностических `data-*` +атрибутах корневого `ha-card`. На боевом плане это 99 resolutions и около +20–27 мс на каждый render во всех четырёх режимах. + +Проверенный на живой странице cache одного результата уменьшил стоимость +принудительного render примерно вдвое, а суммарные Long Tasks pan — на 70 %. +Однако cache «навсегда» неверен: значения должны меняться при изменении +registry, config или доступности привязки. + +### 3.2 Continuous state реактивно перестраивает всю карточку + +`_view`, `_zoom`, hover, draft и большая часть drag/preview state объявлены Lit +state. Их изменение на каждом pointer/animation step планирует полный проход +`HouseplanCard.render()`: комнаты, стены, проёмы, декор, Glow, солнце, +устройства и подписи строятся заново, хотя между соседними шагами меняется +только камера или небольшой интерактивный слой. + +Уже существующий fast path initial Plan snap hover решает один частный случай, +но не создаёт общего контракта для View и редакторов. + +### 3.3 Любой новый объект `hass` считается причиной render + +Карточка не отличает изменение используемой планом entity от изменения одной +из сотен посторонних entities. Вместе с render сейчас выполняется и +операционная обработка HA snapshot: registry authority, reconnect/load, +activity и vacuum histories, device rebuild и visual-continuity snapshot. +Просто вернуть `false` из `shouldUpdate()` недостаточно: это могло бы убрать +видимый render вместе с обязательной обработкой входящего состояния. + +### 3.4 Performance gate проверяет другие сценарии + +Абсолютные `hardMaxMs` уже есть. Пробел состоит не в отсутствии абсолютного +механизма, а в сценариях: `panZoomMs` измеряет один wheel transition, +`stateUpdateMs` меняет участвующую entity, а настоящий drag, View hover, +нерелевантный HA tick и количество тяжёлых render-проходов не проверяются. +Постоянно дорогой путь поэтому может быть одинаковым в base и candidate и +оставаться зелёным. + +## 4. Scope + +В issue входят: + +1. Разделение приёма каждого нового `hass` snapshot, операционной обработки и + решения о визуальной инвалидизации. +2. Явный dependency projection всех HA данных, которые использует план или + открытая UI-поверхность, и пропуск полного render для нерелевантного tick. +3. Cache статической части diagnostics с точной инвалидизацией; публичный + support-report и три `data-*` атрибута остаются актуальными. +4. Единый lightweight live-interaction путь для камеры, hover и непрерывных + preview во View и трёх редакторах. +5. Coalescing pointer/animation updates до не более одного лёгкого paint на + animation frame без повторной шаблонизации тяжёлой сцены. +6. Один полный reconciliation render после commit/cancel жеста; применение + последнего отложенного релевантного HA snapshot в том же итоговом кадре. +7. Сохранение desktop, touch/pen, flat/hidden-iso, kiosk, fixed-floor, + visibility/reconnect и lazy-runtime контрактов. +8. Новый performance-профиль или совместимое расширение harness с настоящими + pointer series, абсолютными потолками, Long Task и структурными assertions. +9. Unit, targeted smoke, canonical screenshots/golden и release artifacts. + +## 5. Non-scope + +Не входят: + +- изменение внешнего вида комнат, стен, проёмов, устройств, подсказок, Glow, + солнечных лучей, пылесосов, декора или hidden iso; +- изменение hit areas, snap tolerance, grid, gesture thresholds, animation + duration/easing, click/double-click/long-press или commit/cancel semantics; +- виртуализация SVG/DOM, spatial index, изменение числа отображаемых объектов; +- оптимизация конкретных geometry algorithms, room labels или decor renderer, + кроме прекращения их повторного вызова без причины; +- изменение persisted config/layout, websocket/backend API, schema, + import/export или миграция данных; +- ослабление существующих relative/absolute performance budgets; +- публичная настройка, feature flag или новое пользовательское сообщение. + +## 6. Контракт входящих HA snapshots + +### 6.1 Приём не равен render + +Каждое присваивание `card.hass = next` обязано обновить актуальную ссылку на HA +и пройти безопасный intake, даже если visual render не нужен. Intake продолжает: + +- отслеживать connection/reconnect и registry authority; +- запускать начальную/повторную загрузку, когда она требуется; +- поддерживать device roster, finite activity runtime и vacuum telemetry/trail; +- обновлять данные, которыми воспользуются event handlers и HA service calls; +- поддерживать visual-continuity lifecycle без публикации половины кадра. + +Пропуск visual render не имеет права пропустить подписку, history point, +terminal activity edge, vacuum sample, reconnect или capability change. + +### 6.2 Dependency projection + +Для решения о render существует один канонический набор зависимостей, а не +отдельные списки в фильтре и в renderer. В него входят как минимум: + +- entity/device/area bindings всех сохранённых маркеров и их `controls`; +- room temperature/humidity sources; +- opening contact/lock references; +- entities в live-text; +- `sun.sun`, light/Glow sources, vacuum source/map/telemetry references; +- entities и registry metadata, необходимые открытому dialog/picker/info card; +- active/disabled/removed registry resolution для bindings; +- top-level HA значения, влияющие на язык, локаль, units, theme, user/write + permissions, capabilities, connection и frontend formatting. + +Entity state считается изменившимся, если для dependency id изменилось +наличие или identity соответствующего HA state object. Это включает изменение +`state`, attributes, timestamps и unavailable/recovery без дорогого deep +comparison. Изменение нерелевантной entity не инвалидирует visual frame. + +Dependency set пересобирается после config/layout/marker/space/dialog/registry +изменения до следующего решения о фильтрации. Если классификатор не может +доказать нерелевантность snapshot, он fail-open: разрешает render. + +### 6.3 Обычный режим и активный жест + +- Вне continuous interaction релевантный HA snapshot публикуется без новой + намеренной задержки — в обычном ближайшем Lit update. +- Нерелевантный HA snapshot не вызывает `HouseplanCard.render()` и не меняет + тяжёлый DOM, но становится актуальным для последующих действий и intake. +- Во время активного pan/pinch/resize/drag/draft релевантный HA snapshot не + прерывает лёгкий live paint. Сохраняется только последний snapshot; сразу + после `pointerup` или `pointercancel` он входит в единственный итоговый full + render. Промежуточные snapshots не проигрываются кадр за кадром. +- `lostpointercapture`, уход со страницы, смена mode/space и structural config + change завершают либо отменяют interaction штатным общим terminal path и не + оставляют pending snapshot навсегда. Структурное изменение не откладывается, + если продолжение жеста с ним небезопасно. +- HA actions и safety checks всегда читают последний принятый `hass`, а не + отложенный visual snapshot. + +## 7. Контракт diagnostics cache + +1. Полный обход marker bindings не выполняется из `_renderBody()` и не + повторяется на camera/hover/editor pointer step. +2. Cache хранит статический результат registry diagnostics и binding counts. + Он инвалидируется при изменении marker/config binding lifecycle, registry + revision/authority, active/disabled/removed metadata или наличия state, + влияющего на binding status. +3. Обычное изменение значения уже доступной entity не инвалидирует binding + counts, если её классификация `active/ha_disabled/orphaned/unverified` не + могла измениться. +4. Три корневых атрибута сохраняют текущие имена и значения: + `data-ha-registry-access`, `data-ha-disabled-bindings`, + `data-ha-unverified-bindings`. +5. Публичный `houseplanDiagnostics()` сохраняет форму и redaction. Динамический + `lastSuccessAgeMs` вычисляется в момент вызова поверх cached core; для его + роста не запускается timer и не инвалидируется plan render. +6. После invalidation новый результат вычисляется не более одного раза до + следующей смены dependency, независимо от количества render requests. + +## 8. Контракт lightweight live-interaction слоя + +### 8.1 Граница тяжёлой сцены + +Тяжёлая сцена — room fills/outlines, physical walls, openings, saved decor, +Glow/sun, device faces, room labels, vacuum trails и hidden-iso geometry — не +перешаблонизируется из-за очередного pointermove или camera animation frame. +Её DOM identity сохраняется в пределах interaction, если нет отдельной +структурной причины для rebuild. + +Вызов `HouseplanCard.render()` считается full render независимо от того, дал ли +Lit затем минимальный DOM diff. Рендер отдельного lightweight child/layer full +render карточки не считается. + +### 8.2 Камера + +Pan, pinch, wheel/camera transition, zoom buttons, room focus и double-fit +обновляют все участвующие SVG `viewBox`, HTML overlay projection и зависящие от +камеры размеры согласованно в одном RAF-coalesced live paint. Flat и hidden iso +не расходятся; tooltip, device layer, room labels, locks, measure labels, +vacuum puck и editor chrome остаются совмещены с планом. + +Canonical `_view`/`_zoom` продолжают отражать реально показанный кадр, чтобы +retarget/cancel/persistence контракты #82/#396/#449 не получили stale start. +На terminal state выполняется один full reconciliation render и штатное +сохранение viewport там, где оно происходило раньше. + +### 8.3 Hover + +Принятый владельцем Q2 включает обычный hover: + +- room fill/physical outline и room tooltip сохраняют нынешние content, + visibility, pointer modality и настройку отключения tooltip; +- device tooltip/LQI/temperature/humidity и текущие CSS hover states остаются; +- координата tooltip может обновляться каждый RAF, не вызывая full render; +- leave, touch suppression, mode/space switch, visibility change и removal + очищают lightweight hover без ghost overlay; +- hover никогда не меняет config/layout и не делает websocket writes. + +### 8.4 Редакторские continuous interactions + +Один и тот же принцип применяется ко всем состояниям, которые меняются на +pointermove до commit: + +| Поверхность | Live-содержимое | +|---|---| +| План | wall chain/rubber-band, snap/conflict marker, column/opening preview и dimensions, wall/room resize preview и measurements, physical move/rotate | +| Устройства | marker drag/position preview, align guides и связанный tooltip | +| Подложка | line/shape draft, decor/furniture/image move/resize/rotate, placement preview, backdrop transform и measurements | + +Pointermove меняет только соответствующий lightweight layer или transform. +Pointerup сохраняет тот же canonical результат, history entry и write, что до +#451; cancel восстанавливает то же исходное состояние и не создаёт write. +Pointerdown может выполнить отдельный full render, если действительно меняет +selection/tool UI, но последующие move-события не повторяют его. + +Все movement sources coalesce: между двумя animation frames применяется +последняя позиция, но commit повторно использует последнюю canonical event +coordinate/state, поэтому coalescing не теряет конечную точку. + +## 9. UX, touch и accessibility + +- Внешний вид до/после settled state должен быть pixel-equivalent с текущим + `dev`; намеренных golden diffs нет. +- Mouse, touch и pen сохраняют pointer capture, pan threshold, pinch ownership, + click suppression и `pointercancel` поведение. +- Hover остаётся только на устройствах с fine hover capability; touch не + получает синтетический ghost hover. +- Keyboard/Escape/Ctrl+Z и focus order не меняются. +- Reduced motion сохраняет atomic camera result; forced colours, dark/light, + kiosk, fixed-floor и read-only не получают отдельной ветки поведения. +- Новых текстов, контролов, ARIA-элементов и i18n keys нет. + +## 10. Модель данных, совместимость и безопасность + +- Persisted config/layout и backend payload не меняются; migration/write-back + отсутствуют. +- Старые планы автоматически получают ускорение после обновления frontend. +- Оптимизация не изменяет HA service calls, permission checks, destructive + confirmations, registry redaction или support-report contents. +- Не вводятся worker, network request, dependency или глобальный shared cache + между экземплярами карточки. +- Несколько карточек на dashboard имеют независимые interaction queues, + dependency sets и diagnostics cache. +- Disconnect удаляет RAF/listener/observer и pending interaction state; warm + remount не переносит незавершённый gesture в новый экземпляр. + +## 11. Performance и наблюдаемость + +### 11.1 Новый профиль + +Добавляется отдельный `large-house-interaction-v1` на существующем +детерминированном large-house fixture, чтобы не менять смысл +`large-house-v1`. Один runner/harness обязан оставаться способен измерить base +до #451 и candidate; новые private поля сначала объявляются optional с честным +fallback либо счётчики собираются внешним instrumentation. + +После warm-up профиль выполняет: + +1. 120 View hover moves по room/device/miss; +2. четыре pan drag по 20 moves и terminal pointerup; +3. camera wheel/transition и pinch series; +4. не менее трёх representative editor series: Plan draw/snap, room resize и + Decor furniture/shape transform; targeted smoke покрывает остальную матрицу; +5. 30 нерелевантных HA ticks и один релевантный tick; +6. релевантный HA tick в середине drag с проверкой deferred-last-wins frame. + +### 11.2 Структурные assertions + +Для измерительного окна после начального settled frame: + +- 120 hover moves: **0** full renders; +- каждое pan/pinch/editor move-series: **0** full renders между start и terminal + event и не более **1** terminal full render; +- 30 нерелевантных HA ticks: **0** full renders; +- один релевантный HA tick вне gesture: ровно **1** full render; +- несколько релевантных ticks внутри gesture: **0** промежуточных и ровно + **1** terminal full render с последним состоянием; +- camera/hover/editor windows: **0** полных marker-binding diagnostic scans; +- static heavy-scene node identity/count, config, layout и websocket writes + остаются стабильны; preview commit/cancel checks выполняются отдельно. + +Assertions fail the runner независимо от timing result. RAF paints и renders +выделенного lightweight child считаются отдельными метриками и не маскируются. + +### 11.3 Абсолютные цели + +Для профиля при тех же CI browser/runtime условиях задаются base-relative +пределы и следующие bootstrap `hardMaxMs`. Они намеренно существенно ниже +опубликованного в issue времени полных updates одного pan-сценария — 3 432 мс — +и согласованы с существующим `panZoomMs: 500 ms`. Для hover потолок 500 мс на +120 moves означает в среднем не более 4,2 мс на событие против замеренных в +issue 98,9 мс на полный update: + +| Метрика | Абсолютный потолок | +|---|---:| +| 120 View hover moves | 500 мс | +| 4 × 20 pan moves + terminal | 500 мс | +| wheel/pinch camera series | 500 мс | +| 120 representative editor moves + terminal | 750 мс | +| 30 нерелевантных HA ticks | 250 мс | + +Для каждого pointer-series дополнительно: `maxSingleLongTaskMs <= 150`, +`longTaskTotalMs <= 300`, `longTaskCount <= 3`. Эти потолки — bootstrap safety +limits, а не разрешение расходовать весь budget; relative comparison остаётся. +Ослабление существующих budgets запрещено. Первый paired Linux artifact должен +быть описан в review/release evidence; ужесточение после него допустимо отдельной +обоснованной правкой, ослабление — только через новый review. + +## 12. Acceptance criteria + +- **AC1 (`unit` + code review; разработчик/ревьюер):** HA intake отделён от + visual invalidation; нерелевантный snapshot обновляет актуальный `hass` и + lifecycle/history, но не вызывает `HouseplanCard.render()`. +- **AC2 (`unit` + targeted smoke; разработчик):** изменение каждой категории + dependency из §6.2 вызывает актуальный render, а изменение посторонней entity + — нет; unknown classifier path fail-open и не оставляет stale UI. +- **AC3 (`unit` + targeted smoke; разработчик):** activity/vacuum samples, + reconnect/load, registry subscription, permissions и action safety работают + при серии skipped visual ticks; action использует последний `hass`. +- **AC4 (`unit` + smoke; разработчик):** diagnostics binding scan отсутствует + в render/pointer path, cache инвалидируется по §7, три `data-*` атрибута и + redacted public report актуальны, `lastSuccessAgeMs` растёт без repaint timer. +- **AC5 (`smoke` + performance; разработчик):** View hover, pan, pinch, + wheel/camera animation и double-fit соблюдают render-count assertions §11.2; + все SVG/HTML/iso layers остаются совмещены на каждом captured frame. +- **AC6 (`smoke` + performance; разработчик):** Plan draw/snap/opening/column, + physical move/rotate, room resize, device drag, decor draft/move/resize/rotate, + furniture/image placement и backdrop transform не вызывают full render на + каждый move; terminal commit/cancel даёт один согласованный full frame. +- **AC7 (`unit` + smoke; разработчик):** серия relevant HA updates во время + gesture применяет только последний snapshot сразу после всех terminal paths; + irrelevant updates не создают pending render. +- **AC8 (`unit` + smoke; разработчик):** coalescing сохраняет конечную pointer + coordinate, snap/geometry result, history, save count, pointer capture, + cancel/undo и no-extra-write контракты для mouse/touch/pen. +- **AC9 (`golden` + canonical screenshots; разработчик/владелец):** settled + View, hover/tooltip, все три редактора, dark/light, forced colours, kiosk, + fixed-floor и hidden iso не имеют непредусмотренных pixel diffs; screenshots + приложены к handoff/review. +- **AC10 (`performance`; разработчик):** новый interaction profile проходит + все structural, absolute и base-relative checks §11; прежние ordinary, + isometric, plan-snap и Glow profiles проходят без ослабления budgets. +- **AC11 (`typecheck` + `unit` + `build`; разработчик):** implementation-loop + gates зелёные; перед beta зелёные golden, smoke и performance по runbook; + Linux CI остаётся каноном полного HA harness. +- **AC12 (`schema/backend/i18n review`; ревьюер):** persisted model, backend, + import/export, HA API, i18n strings и dependencies не изменены; новые caches + bounded per card и очищаются при disconnect. +- **AC13 (`documentation review`; разработчик/ревьюер):** + `demo/performance/README.md` описывает новый профиль, counters, budgets и + локальный запуск; оба changelog содержат пользовательский эффект со ссылкой + на #451. + +## 13. План автотестов + +### 13.1 Unit + +- pure dependency classifier: relevant/irrelevant/missing state, attributes, + all source kinds, top-level locale/theme/units/user/connection and fail-open; +- dependency-set rebuild после marker/config/dialog/registry changes; +- HA intake с skipped render: activity terminal edge, vacuum sample, reconnect, + latest action snapshot и no duplicate processing; +- gesture gate state machine: begin/update coalescing/end/cancel/forced cancel, + latest-relevant-wins и irrelevant-no-pending; +- diagnostics cache keys, one scan per invalidation, state-presence transition, + dynamic age и redaction; +- camera live projection: clamp, anchor, retarget, persistence and cleanup. + +### 13.2 Targeted production-bundle smoke + +Инструментировать full render count, lightweight paints, diagnostics scans, +heavy node identity, writes и visible state. На production bundle пройти: + +- room/device/miss hover and leave; +- mouse pan, wheel, pointercancel; touch pan/pinch and lost capture; +- #449 double-fit и interrupted/retargeted camera transition; +- матрицу interactions из AC6 с несколькими move events, commit и cancel; +- relevant/irrelevant HA ticks вне и внутри gesture; +- space/mode switch, visibility hide/show, disconnect/reconnect и warm remount; +- flat/iso, normal/fixed-floor/kiosk/read-only. + +Synthetic events обязаны доходить до production handlers; `0` событий у +handler не принимается как доказательство `0` renders. + +### 13.3 Golden, performance и release + +- implementation loop: `npm run typecheck`, `npm test`, `npm run build`; +- после изменения `src/**` — canonical screenshots по workflow из `PROCESS.md` + §8 (`demo/docs/capture.mjs`, затем только после проверки `npm run docs:accept`), + inspection и существующие golden; любой diff исследуется; +- перед beta — полный smoke/golden/performance gate по runbook; +- full performance сравнивает candidate/base на одном Linux runner и публикует + отдельный interaction artifact со structural diagnostics; +- три bundle snapshots после release build должны совпадать. + +## 14. План реализации и затрагиваемые файлы + +Ожидаемые точки изменения: + +1. `src/houseplan-card.ts` — HA intake/visual invalidation, diagnostics cache, + gesture terminal reconciliation и подключение live layers. +2. Новый либо существующий узкий `src/*render*`/`src/*interaction*` module — + pure dependency classifier, bounded state machine и RAF scheduler; точные + имена не являются контрактом. +3. `src/houseplan-editor-runtime.ts` — маршрутизация continuous editor previews + в lightweight invalidation без изменения canonical commit logic. +4. Unit tests для classifier/cache/controller и regression tests существующих + camera/editor controllers. +5. Targeted `demo/smoke_*.mjs` — production-bundle render-count и interaction + matrix; обновление smoke runner/package scripts при необходимости. +6. `demo/benchmark_large_house.mjs`, `demo/performance/card-contract.mjs`, новый + budget/profile и evaluator/report plumbing — `large-house-interaction-v1`. +7. `demo/performance/README.md` и оба changelog. + +Реализация сначала выделяет pure contracts и diagnostics cache, затем HA +filter, затем camera/hover live path, затем редакторские consumers. Нельзя +массово снять `state: true`, пока у каждого изменяемого состояния нет +проверенного live paint и terminal reconciliation path. + +## 15. Release-артефакты + +Изменение пользовательски заметно как исправление зависаний, поэтому основной +implementation commit имеет `User-Visible: yes` и в том же коммите обновляет: + +- `docs/CHANGELOG.md`; +- `docs/CHANGELOG.ru.md`. + +Также обновляются: + +- `demo/performance/README.md`; +- budget/profile и machine-readable performance report; +- canonical screenshots/golden evidence без ожидаемого визуального изменения; +- targeted smoke artifacts и full performance comparison. + +Новые user-guide/i18n/security artifacts не требуются. Выпуск — обычной beta +по разделу 8 release runbook; issue не закрывается автором реализации. + +## 16. Риски и снижение + +| Риск | Вероятность / ущерб | Снижение | +|---|---|---| +| Фильтр ошибочно считает relevant HA tick посторонним | средняя / высокий | единый dependency authority, fail-open, матрица всех consumers и dialog states | +| Пропуск render одновременно пропускает activity/vacuum/reconnect intake | высокая / высокий | отдельный intake contract и тесты серий skipped ticks | +| Lightweight DOM расходится с canonical state | средняя / высокий | один state machine, last-event commit и обязательный terminal reconciliation | +| Несколько SVG/HTML/iso слоёв получают разные camera frames | средняя / высокий | один atomic projection writer и frame screenshots | +| Gesture оставляет pending HA/RAF после cancel/disconnect | средняя / высокий | единый terminal cleanup и lifecycle matrix | +| Child render формально скрывает прежнюю тяжёлую работу | средняя / средний | отдельные full/light counters, heavy-node identity и timing gate | +| Новый harness ломает сравнение со старым base | средняя / высокий | новый profile id, optional contract/fallback и тест candidate/base | +| Слишком мягкий timing budget снова пропускает проблему | средняя / высокий | structural assertions обязательны; absolute + relative gates, paired artifact review | + +## 17. Откат + +Откат — revert implementation commit(ов) #451 и нового interaction profile. +Данные и backend не требуют rollback/migration. Если дефект найден только в +одном live interaction, допускается временно вернуть этому interaction прежний +reactive path отдельным аварийным commit, не отключая безопасный diagnostics +cache и HA filter, но такой fallback обязан иметь issue и regression proof. + +## 18. Принято предположительно, поменять свободно + +Ниже технические решения, не являющиеся продуктовым выбором владельца: + +- dependency projection может переиспользовать `RenderDeviceSnapshot`, но имеет + один источник истины и включает зависимости всего сохранённого плана; +- lightweight слой может быть Lit child component, imperative DOM projection + или их комбинацией, если full/light counters и cleanup contracts соблюдены; +- public diagnostics складывается из cached static core и динамического age; +- точные private field/helper names и разбиение unit/smoke файлов свободны; +- representative editor cases живут в performance profile, полная матрица — в + deterministic smoke, чтобы timing fixture не превращался в функциональный + e2e-комбайн. + +Продуктовые решения владельца не предположительны: Q1 — relevant HA updates во +время жеста применяются последним итоговым кадром; Q2 — обычный hover входит в +#451 и сохраняет текущие эффекты через lightweight слой. diff --git a/docs/specs/README.md b/docs/specs/README.md index 8c9de736..7a8b54a3 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -177,6 +177,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным | [#447](https://github.com/Matysh/houseplan-card/issues/447) Наружная грань для мебели и сдвиг декора стрелками | [447-exterior-furniture-snap-keyboard-nudge.md](447-exterior-furniture-snap-keyboard-nudge.md) | | [#448](https://github.com/Matysh/houseplan-card/issues/448) Единый бессрочный переключатель `hp_alpha` | [448-alpha-switch.md](448-alpha-switch.md) | | [#449](https://github.com/Matysh/houseplan-card/issues/449) Двойной клик/тап по свободному фону вписывает весь план | [449-double-fit-all.md](449-double-fit-all.md) | +| [#451](https://github.com/Matysh/houseplan-card/issues/451) Фильтрация render и лёгкий live-слой взаимодействий | [451-render-performance.md](451-render-performance.md) | ## P3 diff --git a/package.json b/package.json index 3da27c68..e3e39b2f 100755 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "benchmark:large-house": "node demo/benchmark_large_house.mjs", "benchmark:large-house-isometric": "node demo/benchmark_large_house.mjs --profile=large-house-isometric-v1", "benchmark:large-house-plan-snap": "node demo/benchmark_large_house.mjs --profile=large-house-plan-snap-v1", + "benchmark:large-house-interaction": "node demo/benchmark_large_house.mjs --profile=large-house-interaction-v1", "benchmark:glow": "node demo/benchmark_glow.mjs", "benchmark:compare": "node demo/performance/compare.mjs", "benchmark:optimize-geometry-preflight": "node demo/benchmark_optimize_geometry_preflight.mjs", diff --git a/scripts/bundle-budget.mjs b/scripts/bundle-budget.mjs index d46bf74a..7cafa949 100644 --- a/scripts/bundle-budget.mjs +++ b/scripts/bundle-budget.mjs @@ -108,8 +108,15 @@ export const LOW_HEADROOM_WARNING_BYTES = 15_000; * после реализации он занимает 295 743 Б gzip. Запас до потолка — 1 257 Б, * до нижней границы полосы — 743 Б, то есть обе стороны остаются дальше * наблюдаемого шума. Общий бюджет 300 000 Б и долг #367 не меняются. + * + * 2026-09-04, #451: потолок поднят 297 000 → 298 000. Терминальная доставка + * последнего HA-снимка и явное управление кадром редактора принадлежат + * основному View: без них пропущенный полный render оставлял бы устаревшее + * состояние после pan/resize. После исправления initial-граф занимает + * 296 717 Б gzip: 1 283 Б сверху и 717 Б до нижней границы полосы. Общий + * бюджет 300 000 Б не меняется; долг ленивого выноса #367 остаётся видимым. */ -export const INITIAL_VIEW_GZIP_CEILING = 297_000; +export const INITIAL_VIEW_GZIP_CEILING = 298_000; export const INITIAL_VIEW_CEILING_BAND = 2_000; /** diff --git a/scripts/mutation-gate.mjs b/scripts/mutation-gate.mjs index 47e097f3..dbb7e9ba 100644 --- a/scripts/mutation-gate.mjs +++ b/scripts/mutation-gate.mjs @@ -105,6 +105,17 @@ function relocateEditorPatch(patch, cardSource, editorSource) { // `find` обязан встречаться в файле ровно один раз: патч, который ложится «куда // попало», проверяет не то, что объявлен проверять. Это контролирует --check. const MUTANT_DEFINITIONS = [ + { + id: 'render-invalidation-renders-irrelevant-ha', + guard: 'node demo/smoke_render_invalidation.mjs', + because: 'an irrelevant HA state row must still enter lifecycle intake without scheduling ' + + 'a full Lit render; the production smoke owns this card/runtime wiring (#451)', + patches: [{ + file: 'src/houseplan-card.ts', + find: ' if (!render) return;', + replace: ' if (false && !render) return;', + }], + }, { id: 'draft-delete-drops-the-promise', guard: 'node demo/smoke_free_walls.mjs', diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts index efe7272d..79b01a9c 100755 --- a/src/houseplan-card.ts +++ b/src/houseplan-card.ts @@ -6,7 +6,7 @@ * 2) LEGACY fallback — baked-in country-house data (src/data/*), coordinates in a 1489×1053 canvas. * The icon layout is stored on the server (houseplan/layout/*), fallback — localStorage. */ -import { LitElement, html, svg, nothing, noChange, TemplateResult, PropertyValues } from 'lit'; +import { LitElement, html, svg, nothing, noChange, TemplateResult, PropertyValues, type PropertyDeclaration } from 'lit'; import { guard } from 'lit/directives/guard.js'; import { repeat } from 'lit/directives/repeat.js'; import './hp-dialog'; @@ -92,9 +92,8 @@ import { type LinearWallSegment, type WallEntry, type WallInterval, innerEdgeSpan, ownEdgeOffsets, thicknessCmAt, } from './wall-thickness'; -import { - checkNodeDistances, checkNodes, checkRoomClearance, checkSegmentLengths, - increasedViolations, type JunctionLimitViolation, type LimitSegment, +import type { + JunctionLimitViolation, JunctionSharedGeometry, LimitSegment, } from './junction-limits'; import { pointOnOpenCut, sanitizeOpenSpans, @@ -305,6 +304,8 @@ import { createRenderDeviceSnapshot, presentationSnapshotKey, renderDeviceSnapshotPositions, type RenderDeviceSnapshot, } from './render-device-snapshot'; +import { RenderLifecycle, intakeHass } from './houseplan-render-lifecycle'; +import type { HassRenderSnapshot } from './render-invalidation'; import { deviceFaceStyle, deviceThemeClass, renderDeviceFace } from './device-face'; import { effectiveDeviceBaseSize } from './device-marker-geometry'; import { renderZigbeeTopologyOverlay } from './zigbee-topology-overlay-bridge'; import { @@ -377,12 +378,15 @@ import { import { openingDefaultLengthCm, openingPlacementPreset, passagePlacementPreviewGeometry, resolveOpeningPlacementResult, sameOpeningPlacementInput, - type OpeningPlacementCore, type OpeningPlacementPreset, type OpeningPlacementType, + type OpeningPlacementPreset, type OpeningPlacementType, } from './opening-placement'; import { buildOpeningDimensionContext, resolveOpeningDimensions, - type OpeningDimension, type OpeningDimensionContext, + type OpeningDimensionContext, } from './opening-dimensions'; +import type { + DeviceDragState, OpeningPlacementCandidate, OpMeasure, RenderOpening, +} from './interaction-types'; import { safeStoredColor } from './color'; import { gridCellFieldToCm, gridCellFieldValue, gridVisualScale, gridVisualUnits, @@ -626,32 +630,6 @@ const capturePointer = (ev: PointerEvent): void => { } }; -/** Ruler labels + the centre-magnet tick. Existing-opening DRAG carries its - * legacy two labels; a new-opening PLACEMENT may additionally carry physical - * line/tick geometry (#238). */ -interface OpMeasureLabel { - x: number; - y: number; - text: string; - dimension?: OpeningDimension; -} - -interface OpMeasure { - labels: OpMeasureLabel[]; - guide: { x: number; y: number; angle: number } | null; -} - -type OpeningPlacementCandidate = Omit & { - face: OpeningFaceOffset; - measure: OpMeasure; -}; - -type RenderOpening = OpeningCfg & { - rx: number; ry: number; rlen: number; - partitionHost?: ResolvedPartitionOpening; - orphanReason?: PartitionOpeningOrphanReason; -}; - interface DeviceInboxDialogState { tab: DeviceInboxCategory; search: string; @@ -678,23 +656,25 @@ type FixedFloorState = FixedFloorSelection | { kind: 'pending'; value: unknown } */ const HANDLE_PAINT_ORDER = ['edges', 'corners'] as const; -interface DeviceDragState { - id: string; - spaceId: string; - displayName: string; - pointerId: number; - source: Element | null; - sx: number; - sy: number; - ox: number; - oy: number; - moved: boolean; - before: DevicePlacement | null; - start: DevicePlacement; -} - export class HouseplanCard extends LitElement { + public requestUpdate(name?: PropertyKey, oldValue?: unknown, options?: PropertyDeclaration): void { + if (name === 'hass' && this.hass && this._liveRt) { + const snapshot = this._visibleDeviceSnapshot || this._candidateDeviceSnapshot; + const render = this._liveRt.hass( + oldValue as HassRenderSnapshot | null | undefined, this.hass, + snapshot ? { entityIds: snapshot.entityIds } : null, + () => intakeHass(this), + ); + if (!render) return; + } + if (name !== undefined) this._terminalFrame = 0; + if (this._editorRuntime?._routeLiveEditorUpdate(name, oldValue)) return; + this._liveRt?.clear(); + super.requestUpdate(name, oldValue, options); + } + public _editorRuntime: import('./houseplan-editor-runtime').HouseplanEditorRuntime | null = null; + private _liveRt: import('./live-interaction-runtime').LiveRuntime | null = null; public _onboardingRuntime: import('./houseplan-onboarding-runtime').HouseplanOnboardingRuntime | null = null; private _editorRuntimeLoadingVisible = false; /** #39: pending large-backdrop decision; set only by the lazy pick flow. */ @@ -1114,7 +1094,7 @@ export class HouseplanCard extends LitElement { state.presented = presented; this._zoom = presented.zoom; this._view = { ...presented.viewBox }; - this.requestUpdate(); + this._liveVp(true); } private _settleCameraTransition(state: CameraTransitionState): void { @@ -1952,6 +1932,7 @@ export class HouseplanCard extends LitElement { private _panLock: 'pan' | 'swipe' | null = null; private _pinchStart: { dist: number; zoom: number } | null = null; private _suppressClick = false; + private _viewportGestureDirty = false; /** * Pointer events from an interactive child may stop before the stage sees * them. Track touch contacts on the card in capture phase, so the second @@ -2482,8 +2463,6 @@ export class HouseplanCard extends LitElement { _space: { state: true }, _layout: { state: true }, _devices: { state: true }, - _tip: { state: true }, - _hoverRoom: { state: true }, _selId: { state: true }, _toast: { state: true }, _serverCfg: { state: true }, @@ -2554,8 +2533,6 @@ export class HouseplanCard extends LitElement { _backupImportDialog: { state: true }, _importDialog: { state: true }, _markerDialog: { state: true }, - _zoom: { state: true }, - _view: { state: true }, }; public connectedCallback(): void { @@ -2580,6 +2557,7 @@ export class HouseplanCard extends LitElement { this._showToast(this._t('toast.locale_load_failed')); }); super.connectedCallback(); + void this._ensureLiveRuntime().catch(() => this.requestUpdate()); this._pointerModality.connect(this.ownerDocument.defaultView); const PointerHoverObserver = this.ownerDocument.defaultView?.MutationObserver; if (PointerHoverObserver) { @@ -2644,6 +2622,8 @@ export class HouseplanCard extends LitElement { } public disconnectedCallback(): void { + this._liveRt?.dispose(); + this._editorRuntime?._disposeLiveEditor(); this._clearRoomFocus(true); this._cancelDangerConfirm(); // HA normally changes the route before removing the old Lovelace tree. @@ -3817,6 +3797,7 @@ export class HouseplanCard extends LitElement { private _cfgEpoch = 0; /** Exact settings-only Area lifecycle replacement which may reuse geometry. */ private _cfgEpochPreservedConfig: ServerConfig | null = null; + private _terminalFrame: 0 | 1 | 2 = 0; // 1=restored cancel, 2=deferred HA private _modelCache: { key: string; model: SpaceModel[] } | null = null; private _emptySpaceStateActive = false; private _decorSnapCache: { @@ -4052,6 +4033,7 @@ export class HouseplanCard extends LitElement { const preserveGeometry = this._cfgEpochPreservedConfig === this._serverCfg; this._cfgEpochPreservedConfig = null; if (!preserveGeometry) this._cfgEpoch++; + this._renderLife.invalidate(); } this._syncEmptySpaceState(); // #417: losing the only renderable space while a decision is pending must @@ -4062,28 +4044,18 @@ export class HouseplanCard extends LitElement { this._cancelDangerConfirm(); } if (changed.has('hass') && this.hass) { - this._hassSequence++; - this._renderSnapshotAt = Date.now(); - this._continuity.note('hass-snapshot'); - this._ensureHaRegistryAuthority(); - this._planHassMemo = null; - this._hookConnection(); - if (!this._loadOk && !this._loading && this._loadTries < 8) { - this._loadFromServer(); - } - // Devices must exist before their first state snapshot is classified. - // Otherwise the first real off->on edge after mount becomes the baseline. - this._maybeRebuildDevices(); - this._vacTick(); - this._activityTick(); + const snapshot = this._visibleDeviceSnapshot || this._candidateDeviceSnapshot; + this._renderLife.observe(changed.get('hass') as HassRenderSnapshot | null | undefined, this.hass, + snapshot ? { entityIds: snapshot.entityIds } : null, () => intakeHass(this)); } if (this._continuity.hasCompleteFrame && this._continuity.state === 'steady') { this._continuity.refreshCompleteFrame(this._visualFrameFingerprint()); } this._captureRenderDeviceSnapshot(); } - protected updated(): void { + this._liveRt?.commit(); + this._editorRuntime?._commitLiveEditor(); this._pruneDevicePressFeedback(); this._syncDayCycleClock(); this._warmSnapshot(); // DEV-B703-03: the memo follows what is on screen @@ -4531,11 +4503,26 @@ export class HouseplanCard extends LitElement { private _haRegistryConnection: any = null; private _haRegistryRev = -1; private _haBindingCacheKey = ''; - private _planHassMemo: { hass: any; sig: string; active: any; full: any } | null = null; + private _planHassMemo: { hass: unknown; sig: string; active: unknown; full: unknown } | null = null; + private _renderLife = new RenderLifecycle(); + private _liveEditorPaintCount = 0; + + private async _ensureLiveRuntime(): Promise { + if (this._liveRt) return; + const module = await import('./live-interaction-runtime'); + this._liveRt = new module.LiveRuntime(this); + if (this.isConnected) this.requestUpdate(); + } + + private _liveVp(now = false): void { this._liveRt ? this._liveRt.viewport(now) : this.requestUpdate(); } + private _syncLiveHover(): void { this._liveRt ? this._liveRt.hover() : this.requestUpdate(); } + /** Bypass live routing so a gesture terminal publishes the last HA frame. */ + private _flushHa(): void { if (this._liveRt?.take()) { this._terminalFrame = 2; super.requestUpdate(); } } private _onHaRegistryUpdate = (): void => { const snapshot = haRegistrySnapshot(this.hass); if (snapshot.revision === this._haRegistryRev && this._devices.length) return; this._haRegistryRev = snapshot.revision; + this._renderLife.invalidate(); this._planHassMemo = null; this._regSignature = ''; this._maybeRebuildDevices(); @@ -4549,6 +4536,7 @@ export class HouseplanCard extends LitElement { this._haRegistryConnection = connection; this._haRegistryRev = -1; this._haBindingCacheKey = ''; + this._renderLife.invalidate(); this._areaSnapshotCleanupCandidates.clear(); this._planHassMemo = null; this._haRegistryRelease = acquireHaRegistries(this.hass, this._onHaRegistryUpdate); @@ -4749,17 +4737,9 @@ export class HouseplanCard extends LitElement { registry: ReturnType & { lastSuccessAgeMs: number | null }; bindings: Record; } { - const registry = haRegistryDiagnostics(this.hass); - const bindings: Record = { - active: 0, - ha_disabled: 0, - orphaned: 0, - unverified: 0, - }; - for (const marker of this._markers) { - if (marker.removed || marker.binding === 'virtual') continue; - bindings[this._bindingStatus(marker.binding).kind]++; - } + const { registry, bindings } = this._renderLife.diagnostics( + this.hass, this._markers, (binding) => this._bindingStatus(binding), + ); return { registry: { ...registry, @@ -5420,6 +5400,7 @@ export class HouseplanCard extends LitElement { private _cancelDeviceDrag(): boolean { const drag = this._deviceDrag; if (!drag) return false; + this._editorRuntime?._cancelPointerMove('device'); this._deviceDrag = null; try { if (drag.source?.hasPointerCapture?.(drag.pointerId)) { @@ -6444,6 +6425,7 @@ export class HouseplanCard extends LitElement { } this._zoom = z; this._view = next; + this._liveVp(); return true; } @@ -6489,8 +6471,7 @@ export class HouseplanCard extends LitElement { private _finishBootSettled(): void { if (!this._booting) return; - // `_applyView()` is reactive: wait one paint opportunity, refit once more, - // and only then remove the veil. This prevents a default-view reveal. + // Refit once more before removing the veil. This prevents a default-view reveal. this._refitView(); this._booting = false; this._bootSettling = false; @@ -6639,6 +6620,7 @@ export class HouseplanCard extends LitElement { } this._zoom = result.target.zoom; this._view = { ...result.target.viewBox }; + this._liveVp(); } private _onWheel(ev: WheelEvent): void { @@ -6883,6 +6865,7 @@ export class HouseplanCard extends LitElement { const cx = (pts[0].x + pts[1].x) / 2 - r.left; const cy = (pts[0].y + pts[1].y) / 2 - r.top; this._zoomAt(cx, cy, this._pinchStart.zoom * scale); + this._viewportGestureDirty = true; this._suppressClick = true; this._saveZoom(); } else if (this._panStart) { @@ -6925,11 +6908,14 @@ export class HouseplanCard extends LitElement { }, fit, ); + this._viewportGestureDirty = true; + this._liveVp(); } } } private _stagePointerLeave(ev: PointerEvent): void { + this._editorRuntime?._cancelPointerMove('markup-hover'); if (this._mode === 'decor') this._editorRuntime?._furnPointerLeave(ev); if (!this._markup) return; if (this._tool === 'opening') { @@ -6941,6 +6927,8 @@ export class HouseplanCard extends LitElement { } private _stagePointerUp(ev: PointerEvent): void { + this._flushHa(); + this._editorRuntime?._cancelPointerMove('markup-hover'); const acceptedRoom = acceptedRoomFitGesture( this._roomPointer, ev.pointerId, @@ -7005,15 +6993,10 @@ export class HouseplanCard extends LitElement { return; } if (this._decorMove?.pid === ev.pointerId) { - if (this._decorMove.moved) { - this._recordGeometry(this._t('history.decor_move'), this._decorMove.before); - this._saveConfig(); - } - this._decorMove = null; + this._editorRuntimeOrThrow()._dmUp(); return; } if (this._editorRuntime?._furnPointerUp(ev)) return; - const viewportGestureEnded = !!this._pinchStart || this._panLock === 'pan'; this._pointers.delete(ev.pointerId); if (this._pointers.size < 2) this._pinchStart = null; if (this._pointers.size === 0) { @@ -7022,7 +7005,10 @@ export class HouseplanCard extends LitElement { // reset click suppression on the next tick (so that a click right after a pan does not fire) if (this._suppressClick) setTimeout(() => (this._suppressClick = false), 0); } - if (viewportGestureEnded && this._pointers.size === 0 && !acceptedRoom) this.requestUpdate(); + if (this._viewportGestureDirty && this._pointers.size === 0 && !acceptedRoom) { + this._viewportGestureDirty = false; + this.requestUpdate(); + } if (acceptedRoom) this._fitRoom(acceptedRoom); } @@ -7082,9 +7068,17 @@ export class HouseplanCard extends LitElement { }; capturePointer(ev); this._tip = null; + this._syncLiveHover(); } private _pointerMove(ev: PointerEvent, d: DevItem): void { + if (this._mode !== 'devices') return; + const drag = this._deviceDrag; + if (!drag || drag.id !== d.id || drag.pointerId !== ev.pointerId) return; + this._editorRuntimeOrThrow()._queuePointerMove('device', () => this._pointerMoveNow(ev, d)); + } + + private _pointerMoveNow(ev: PointerEvent, d: DevItem): void { if (this._mode !== 'devices') return; const drag = this._deviceDrag; if (!drag || drag.id !== d.id || drag.pointerId !== ev.pointerId) return; @@ -7110,12 +7104,12 @@ export class HouseplanCard extends LitElement { const ny = clampCanvasR(drag.oy + dy); this._previewDevicePlacement(d.id, this._devicePlacementForCanvas(d, nx, ny)); } - private _pointerUp(ev: PointerEvent, d: DevItem): void { clearTimeout(this._holdTimer); if (this._mode !== 'devices') return; const drag = this._deviceDrag; if (!drag || drag.id !== d.id || drag.pointerId !== ev.pointerId) return; + this._editorRuntimeOrThrow()._flushPointerMove('device'); this._deviceDrag = null; const after = devicePlacement(this._layout, d.id); if (!drag.moved || after === null || sameDevicePlacement(after, drag.start)) { @@ -7150,7 +7144,6 @@ export class HouseplanCard extends LitElement { this.requestUpdate(); }); } - private _pointerCancel(ev: PointerEvent, d: DevItem): void { if (this._deviceDrag?.id !== d.id || this._deviceDrag.pointerId !== ev.pointerId) return; this._cancelDeviceDrag(); @@ -7198,6 +7191,7 @@ export class HouseplanCard extends LitElement { if (suspend) this._pointerModality.suspend(); if (this._tip) this._tip = null; if (this._hoverRoom) this._hoverRoom = null; + this._syncLiveHover(); } /** The latest real pointer event, not a page-global first-touch latch, owns hover. */ @@ -7321,6 +7315,7 @@ export class HouseplanCard extends LitElement { if (!this._pointerModality.hoverEnabled) return; if (this._drag || this._deviceDrag) return; this._tip = { x: ev.clientX, y: ev.clientY, title, meta, lqi, temp, hum, room }; + this._syncLiveHover(); } /** Room pointer preflight stays authoritative even when its tooltip is off. */ @@ -7328,7 +7323,7 @@ export class HouseplanCard extends LitElement { if (this._mode !== 'view') return false; this._notePointer(ev); if (showRoomTooltipOf(this._settings)) return true; - if (this._tip?.room) this._tip = null; + if (this._tip?.room) { this._tip = null; this._syncLiveHover(); } return false; } @@ -7793,10 +7788,10 @@ export class HouseplanCard extends LitElement { } /** #329 П1-П5 over one space. Pure input, no side effects. */ - private _junctionLimitViolations( - config: any, spaceId: string, sharedGeometry?: any, - ): JunctionLimitViolation[] { - return this._editorRuntimeOrThrow()._junctionLimitViolations(config, spaceId, sharedGeometry); + private _junctionLimitViolations(config: unknown, spaceId: string, + sharedGeometry?: JunctionSharedGeometry | null, + roomIds?: ReadonlySet): JunctionLimitViolation[] { + return this._editorRuntimeOrThrow()._junctionLimitViolations(config, spaceId, sharedGeometry, roomIds); } /** Localised refusal text for the first violation a write introduces. */ @@ -7806,7 +7801,7 @@ export class HouseplanCard extends LitElement { /** #329: violations this write would ADD; inherited ones stay untouched. */ private _junctionLimitsIntroduced( - candidate: any, previousConfig: any, spaceId: string, + candidate: ServerConfig, previousConfig: ServerConfig, spaceId: string, ): JunctionLimitViolation[] { return this._editorRuntimeOrThrow()._junctionLimitsIntroduced(candidate, previousConfig, spaceId); } @@ -7861,16 +7856,21 @@ export class HouseplanCard extends LitElement { /** Browser/OS cancellation is an aborted transaction, never a commit. */ private _stagePointerCancel(ev: PointerEvent): void { + this._flushHa(); + this._editorRuntime?._cancelPointerMove('markup-hover'); if (this._roomPointer?.pointerId === ev.pointerId) this._roomPointer = null; this._doubleFit.clear(); if (this._editorRuntime) return this._editorRuntime._stagePointerCancel(ev); - const viewportGestureEnded = !!this._pinchStart || !!this._panStart; this._pointers.delete(ev.pointerId); + this._pointers.delete(ev.pointerId); if (this._pointers.size < 2) this._pinchStart = null; if (this._pointers.size === 0) { this._panStart = null; this._panLock = null; this._swipeStart = null; } - if (viewportGestureEnded && this._pointers.size === 0) this.requestUpdate(); + if (this._viewportGestureDirty && this._pointers.size === 0) { + this._viewportGestureDirty = false; + this.requestUpdate(); + } } private _applyGeometryState( @@ -8245,8 +8245,9 @@ export class HouseplanCard extends LitElement { /** Fixed-topology wall handles. Disabled geometry remains visible and * explains why it cannot start a gesture; the old corner scale is gone. */ - private _renderResizeLayer(view: { x: number; y: number; w: number; h: number }): TemplateResult { - return this._editorRuntimeOrThrow()._renderResizeLayer(view); + private _renderResizeLayer(view: { x: number; y: number; w: number; h: number }, + roomIds?: readonly string[]): TemplateResult { + return this._editorRuntimeOrThrow()._renderResizeLayer(view, roomIds); } /** Openings of the current space in render units. */ @@ -8448,7 +8449,8 @@ export class HouseplanCard extends LitElement { } private _decorMoveUpdate(ev: PointerEvent): void { - return this._editorRuntimeOrThrow()._decorMoveUpdate(ev); + this._editorRuntimeOrThrow()._queuePointerMove('decor-move', () => + this._editorRuntimeOrThrow()._decorMoveUpdate(ev)); } private _decorNudge(renderDx: number, renderDy: number): boolean { @@ -8538,10 +8540,12 @@ export class HouseplanCard extends LitElement { } private _dtMove(ev: PointerEvent): void { - return this._editorRuntimeOrThrow()._dtMove(ev); + this._editorRuntimeOrThrow()._queuePointerMove('decor-transform', () => + this._editorRuntimeOrThrow()._dtMove(ev)); } private _dtUp(): void { + this._editorRuntimeOrThrow()._flushPointerMove('decor-transform'); return this._editorRuntimeOrThrow()._dtUp(); } @@ -8709,8 +8713,10 @@ export class HouseplanCard extends LitElement { */ private _renderTextFrame(view: { x: number; y: number; w: number; h: number }): TemplateResult | typeof nothing { const sh = this._dtSel; - const b = this._dtBox; - if (!sh || !b || b.id !== sh.id) return nothing; + if (!sh) return nothing; + if (sh.kind === 'text' && this._dtBox?.id !== sh.id) return nothing; + const b = sh.kind === 'text' ? this._dtBox : this._decorBoxOf(sh); + if (sh.kind !== 'line' && !b) return nothing; // Two radii, one gesture (owner 2026-08-05: «уменьшить в 4 раза»). `hr` // is the HIT radius — unchanged, still 1.8 % of the visible view, still // finger-sized at any zoom — carried by an invisible circle. `kr` is what @@ -8729,6 +8735,7 @@ export class HouseplanCard extends LitElement { `)} ` as unknown as TemplateResult; } + if (!b) return nothing; const [ax, ay] = this._dtPivot(sh); const ang = Number(sh.angle) || 0; const corners: [number, number, string][] = [ @@ -8761,7 +8768,7 @@ export class HouseplanCard extends LitElement { ` as unknown as TemplateResult; } - private _renderDecorLayer(): TemplateResult { + private _renderDecorLayer(onlyId?: string | null): TemplateResult { const W = NORM_W, H = this._decorH; const editing = this._mode === 'decor'; const erasing = editing && this._decorTool === 'erase'; @@ -8779,7 +8786,7 @@ export class HouseplanCard extends LitElement { const furnitureScreenScale = this._renderProjection === 'iso' ? 1 : furniturePlanScreenScale( stage?.clientWidth, stage?.clientHeight, planView.w, planView.h, ); - const shapes = this._decorList.map((sh) => { + const shapes = this._decorList.filter((sh) => onlyId === undefined || sh.id === onlyId).map((sh) => { const cls = 'dshape' + (editing && this._decorSel === sh.id ? ' dsel' : ''); const style = this._decorResolvedStyle(sh); const strokeWidth = this._decorWidthUnits(sh); @@ -9187,7 +9194,7 @@ export class HouseplanCard extends LitElement { space: SpaceModel, roomId: string, openCuts: number[][] = this._openCuts(), - roomWalls = this._wallUnionGeometry()?.roomGeom, + roomWalls: ReturnType['roomGeom'] = this._wallUnionGeometry()?.roomGeom, multiWallNodes = this._wallUnionGeometry()?.multiWallNodes, ): number[][] | null { const cutsKey = openCuts.map((cut) => cut.join(',')).join(';'); @@ -9724,19 +9731,20 @@ export class HouseplanCard extends LitElement { private _renderRoomHoverFill( paths: { fillD: string; outlineD: string } | null, ): TemplateResult { - if (!paths) return svg`` as unknown as TemplateResult; return svg` - + ` as unknown as TemplateResult; } private _renderRoomHoverOutline( paths: { fillD: string; outlineD: string } | null, ): TemplateResult { - if (!paths) return svg`` as unknown as TemplateResult; return svg` - - + + ` as unknown as TemplateResult; } @@ -9883,7 +9891,7 @@ export class HouseplanCard extends LitElement { private _markupMove(ev: MouseEvent): void { if (!this._markup || !this._editorRuntime) return; - return this._editorRuntime._markupMove(ev); + this._editorRuntimeOrThrow()._queuePointerMove('markup-hover', () => this._editorRuntime!._markupMove(ev)); } /** One resolved architectural candidate shared by hover and click. */ @@ -11292,6 +11300,9 @@ export class HouseplanCard extends LitElement { private _renderBody(): TemplateResult | typeof nothing | typeof noChange { if (!this._config || !this.hass) return nothing; + const terminalFrame = this._terminalFrame; + this._terminalFrame = 0; + if (terminalFrame === 1) return noChange; const localeGate = this._syncDangerConfirmLocaleGate(); if (localeGate === 'cold') return languageLoadingTemplate(); if (localeGate === 'warm') return noChange; @@ -11312,7 +11323,9 @@ export class HouseplanCard extends LitElement { } if (editorRuntimeRequested && !this._editorRuntime) void this._ensureEditorRuntime(); const model = this._model; - const diagnostics = this.houseplanDiagnostics(); + const diagnostics = this._renderLife.diagnostics( + this.hass, this._markers, (binding) => this._bindingStatus(binding), + ); const fixed = this._fixedFloorState(model); if (fixed.kind === 'pending') { return html` @@ -11401,9 +11414,6 @@ export class HouseplanCard extends LitElement { const stageBg = this._editing ? '' : this._stageBg(disp); // opening rulers: the drag of an existing one OR the placement preview const opMeasure = this._opMeasureView; - const decorMeasure = this._decorMeasure; - const bdLive = this._bdLive; - const furnLive = this._editorRuntime?._furnLive() ?? null; const editorChromeMode = this._mode === 'view' ? this._editorChromeMode : this._mode; const roomHover = this._roomHoverPaths(space); const backdropHref = space.bg ? this._display(space.bg.href) : ''; @@ -11560,12 +11570,13 @@ export class HouseplanCard extends LitElement {
    - ${iso && isoLayers?.structural ? svg`` : nothing} ${this._renderActiveChainInk()}` : nothing} - ${this._markup ? svg`${this._renderPlanSnapOverlay()}` : nothing} ${disp.hideOpenings && !this._markup ? nothing @@ -11803,14 +11815,15 @@ export class HouseplanCard extends LitElement { backdrop editor, where rooms and devices are pointer-inert. */} ${this._renderBackdropFrame(view)} ${this._renderTextFrame(view)} + - ${iso && isoLayers?.structural ? svg` - ` : nothing}${renderZigbeeTopologyOverlay({ hass: this.hass, settings: this._settings, devices: this._renderDevices, registry: this._haRegistry, currentSpace: space.id, viewKey: view, view: this._mode === 'view', kiosk: this._kiosk })} @@ -11825,7 +11838,7 @@ export class HouseplanCard extends LitElement { space-card, so the two renderers agree. The compatibility base is resolved before `iconCqw`; only the per-device and kiosk multipliers still feed --dev-size. */} -
    +
    ${devs.map((d) => this._renderDevice(d, view, showLqi))} ${this._renderVacuums(this._renderVacuumDevices, view, space.id)} ${this._renderVacFit(view)} @@ -11835,50 +11848,12 @@ export class HouseplanCard extends LitElement { : nothing} ${this._markup ? space.rooms.map((r) => this._renderRoomGear(r, space, view)) : nothing}
    - ${this._measureAnchor - ? html`
    ${this._renderMeasureLabel(view)}
    ` - : nothing} - ${this._resize?.liveLabels - ? html`
    ${this._resize?.liveLabels?.map((l) => html`
    ${l.text}
    `)}
    ` - : nothing} - ${opMeasure - ? html`
    ${opMeasure.labels.map((l) => html`
    ${l.text}
    `)}
    ` - : nothing} +
    ${this._wallDialog - ? html`
    ${this._renderWallThickDialog()}
    ` - : nothing} - ${decorMeasure - ? html`
    ${decorMeasure.text}
    ` - : nothing} - ${furnLive - ? html`
    ${furnLive.map((l) => html`
    ${l.text}
    `)}
    ` - : nothing} - ${bdLive - ? html`
    ${bdLive.text}
    ` + ? html`
    ${this._renderWallThickDialog()}
    ` : nothing}
    - ${this._zoom > 1 - ? html`
    ${Math.round(this._zoom * 100)}%
    ` - : nothing} + ${this._renderFarHint()} ${this._renderHomeArrow()} ${this._renderEditorRuntimeLoading()} @@ -11938,21 +11913,7 @@ export class HouseplanCard extends LitElement { ${this._backupExportDialog ? this._editorRuntime ? this._renderBackupExportDialog() : nothing : nothing} ${this._backupImportDialog ? this._editorRuntime ? this._renderBackupImportDialog() : nothing : nothing} ${this._importDialog ? this._onboardingRuntime ? this._renderImportDialog() : nothing : nothing} - ${this._tip - ? html`
    - ${this._tip.title}${this._tip.meta ? html`${this._tip.meta}` : nothing} - ${this._tip.temp != null - ? html`${this._t('tip.temp_avg')} ${this._tip.temp}°` - : nothing} - ${this._tip.hum != null - ? html`${this._t('tip.hum_avg')} ${this._tip.hum}%` - : nothing} - ${this._tip.lqi != null - ? html`${this._t('tip.lqi')} - ${this._tip.lqi}` - : nothing} -
    ` - : nothing} + ${this._kiosk && !this._hasFixedFloor && this._kioskDots && this._model.length > 1 ? html`
    ${this._model.map((m) => html``)} @@ -12493,7 +12454,7 @@ export class HouseplanCard extends LitElement { if (pucks.length && !this._vacRaf) this._vacRafLoop(); if (!pucks.length && !trails.length) return nothing; return html` - ${trails.length ? svg`${trails}` : nothing} + ${trails.length ? svg`${trails}` : nothing} ${pucks}`; } @@ -12834,6 +12795,45 @@ export class HouseplanCard extends LitElement { : nothing}
    `; } + /** Pointer-owned HTML painted by the live child renderer, outside the host Lit tree. */ + public _renderLiveEditorMeasurements(value: unknown): TemplateResult | typeof nothing { + const view = value as { x: number; y: number; w: number; h: number }; + const opMeasure = this._opMeasureView, decorMeasure = this._decorMeasure, bdLive = this._bdLive, + furnLive = this._editorRuntime?._furnLive() ?? null; + return html` + ${this._measureAnchor + ? html`
    ${this._renderMeasureLabel(view)}
    ` + : nothing} + ${this._resize?.liveLabels + ? html`
    ${this._resize.liveLabels.map((l) => html`
    ${l.text}
    `)}
    ` + : nothing} + ${opMeasure + ? html`
    ${opMeasure.labels.map((l) => html`
    ${l.text}
    `)}
    ` + : nothing} + ${decorMeasure + ? html`
    ${decorMeasure.text}
    ` + : nothing} + ${furnLive + ? html`
    ${furnLive.map((l) => html`
    ${l.text}
    `)}
    ` + : nothing} + ${bdLive + ? html`
    ${bdLive.text}
    ` + : nothing} + `; + } /** Where the live measurement starts: the last outline point, or the first split point. */ private get _measureAnchor(): number[] | null { @@ -13011,8 +13011,8 @@ export class HouseplanCard extends LitElement { return this._editorRuntimeOrThrow()._renderOpeningPlacementPreview(); } - private _renderOpenings(disp: SpaceDisplay): TemplateResult { - const items = this._openingsR; + private _renderOpenings(disp: SpaceDisplay, onlyIds?: readonly string[]): TemplateResult { + const items = onlyIds ? this._openingsR.filter((item) => onlyIds.includes(item.id)) : this._openingsR; if (!items.length) return svg``; const space = this._spaceModel(); if (!space) return svg``; @@ -13020,7 +13020,7 @@ export class HouseplanCard extends LitElement { const walls = this._spaceWalls; const openCuts = this._openCuts(); const openingWallIndex = this._openingWallIndexFor(space, openCuts).value; - return svg`${items.map((o) => { + return svg`${items.map((o) => { if (o.orphanReason) return svg` this._opPointerUp(e, o)} @pointercancel=${(e: PointerEvent) => this._opPointerUp(e, o)}> `; - })}`; + })}`; } /** Padlock badges for door-like openings with a lock entity. */ diff --git a/src/houseplan-editor-runtime.ts b/src/houseplan-editor-runtime.ts index 3a232901..ab1d2461 100644 --- a/src/houseplan-editor-runtime.ts +++ b/src/houseplan-editor-runtime.ts @@ -11,6 +11,13 @@ import { guard } from 'lit/directives/guard.js'; import { renderVacuumMapsSection } from './editors/vacuum-maps-section'; import { calibrationTarget, planVacuumFit } from './vacuum-route-edit'; import { repeat } from 'lit/directives/repeat.js'; +import { + cancelHouseplanPointerMove, flushHouseplanPointerMove, queueHouseplanPointerMove, +} from './pointer-move-queue'; +import { + commitHouseplanEditor, disposeHouseplanEditor, measureHouseplanDecorText, + routeHouseplanEditorUpdate, +} from './live-editor'; import './hp-dialog'; import type { HpDialog } from './hp-dialog'; import type { HpConfirmRequest } from './danger-confirm'; @@ -54,6 +61,7 @@ import { ResizeController, type ResizeProjectionResult, } from './resize-controller'; +import { resizeLiveCandidateSpace, resizeLiveJunctionRoomIds } from './resize-live-preflight'; import { placeResizeAreaLabel, resizeMeasuredEdges, type ResizeAreaPlacement, @@ -97,8 +105,8 @@ import { innerEdgeSpan, ownEdgeOffsets, thicknessCmAt, } from './wall-thickness'; import { - checkNodeDistances, checkNodes, checkRoomClearance, checkSegmentLengths, - increasedViolations, type JunctionLimitViolation, type LimitSegment, + junctionLimitViolations, increasedViolations, + type JunctionLimitViolation, type JunctionSharedGeometry, type LimitSegment, } from './junction-limits'; import { pointOnOpenCut, sanitizeOpenSpans, @@ -347,12 +355,13 @@ import { import { openingDefaultLengthCm, openingPlacementPreset, passagePlacementPreviewGeometry, resolveOpeningPlacementResult, sameOpeningPlacementInput, - type OpeningPlacementCore, type OpeningPlacementPreset, type OpeningPlacementType, + type OpeningPlacementPreset, type OpeningPlacementType, } from './opening-placement'; import { buildOpeningDimensionContext, resolveOpeningDimensions, - type OpeningDimension, type OpeningDimensionContext, + type OpeningDimensionContext, } from './opening-dimensions'; +import type { OpeningPlacementCandidate, OpMeasure, RenderOpening } from './interaction-types'; import { safeStoredColor } from './color'; import { gridCellFieldToCm, gridCellFieldValue, gridVisualScale, gridVisualUnits, @@ -794,32 +803,6 @@ const capturePointer = (ev: PointerEvent): void => { } }; -/** Ruler labels + the centre-magnet tick. Existing-opening DRAG carries its - * legacy two labels; a new-opening PLACEMENT may additionally carry physical - * line/tick geometry (#238). */ -interface OpMeasureLabel { - x: number; - y: number; - text: string; - dimension?: OpeningDimension; -} - -interface OpMeasure { - labels: OpMeasureLabel[]; - guide: { x: number; y: number; angle: number } | null; -} - -type OpeningPlacementCandidate = Omit & { - face: OpeningFaceOffset; - measure: OpMeasure; -}; - -type RenderOpening = OpeningCfg & { - rx: number; ry: number; rlen: number; - partitionHost?: ResolvedPartitionOpening; - orphanReason?: PartitionOpeningOrphanReason; -}; - interface DeviceInboxDialogState { tab: DeviceInboxCategory; search: string; @@ -990,10 +973,9 @@ export interface HouseplanEditorHostPort { _importQueue: string[]; _importTotal: number; _infoCard: DevItem | null; - _innerRoomContour: (space: SpaceModel, roomId: string, openCuts?: number[][], roomWalls?: any, multiWallNodes?: MultiWallNodeMap | null | undefined) => number[][] | null; - _junctionLimitViolations: ( - config: unknown, spaceId: string, sharedGeometry?: unknown, - ) => JunctionLimitViolation[]; + _innerRoomContour: (space: SpaceModel, roomId: string, openCuts?: number[][], roomWalls?: ReturnType['roomGeom'], multiWallNodes?: MultiWallNodeMap | null | undefined) => number[][] | null; + _junctionLimitViolations: (config: unknown, spaceId: string, + sharedGeometry?: JunctionSharedGeometry | null, roomIds?: ReadonlySet) => JunctionLimitViolation[]; _isVacDev: (d: DevItem) => boolean; _kiosk: boolean; _kioskDialog: boolean; @@ -1119,6 +1101,7 @@ export interface HouseplanEditorHostPort { _serverCfg: ServerConfig | null; _serverStorage: boolean; _settings: { exclude_integrations?: string[]; group_lights?: boolean; show_all?: boolean; filter_seeded?: boolean; icon_rules?: { pattern: string; icon: string; }[]; show_room_tooltip?: boolean; zigbee_topology?: { enabled?: boolean; z2m_base_topics?: string[] }; }; + _terminalFrame: 0 | 1 | 2; _settingsDialog: { colors: FillColors; glowRadius: number; bgColor: string | null; northDeg: number | null; bgMode: "static" | "daynight"; sunRays: boolean; showRoomTooltip: boolean; zigbeeTopology: ZigbeeTopologySettings; busy: boolean; } | null; _supportDialog: SupportDialogState | null; _showAll: boolean; @@ -1163,6 +1146,7 @@ export interface HouseplanEditorHostPort { _vacSourceResolution: (d: DevItem, includeAllCameras?: boolean, planHass?: any) => VacSourceResolution; _vacSrvTrails: Record; _view: { x: number; y: number; w: number; h: number; } | null; + _viewportGestureDirty: boolean; _viewModeSnap: { space: string; zoom: number; cx?: number; cy?: number; } | null; _viewOr: (vb: number[]) => { x: number; y: number; w: number; h: number; }; _viewPreference: Record; @@ -1202,10 +1186,12 @@ export interface HouseplanEditorHostPort { export const EDITOR_RUNTIME_FINGERPRINT = '__HOUSEPLAN_SOURCE_FINGERPRINT__'; export class HouseplanEditorRuntime { - /** #330: baseline limits are stable across one resize gesture. */ private _junctionBaselineCache = new WeakMap(); + private _resizePreviewNodes: MultiWallNodeMap | null = null; + private _resizeBaselineLimits: JunctionLimitViolation[] = []; + private _resizeBaseFrameStable = true; private _supportExpiryTimer?: number; private _supportPreviewGeneration = 0; private _decorAssetGuardReplace: boolean | null = null; @@ -1235,7 +1221,16 @@ export class HouseplanEditorRuntime { ResizePreview, ResizeLiveLabel[], SpaceGeometryState, ResizeWallUnion, ResizeWallArtifact >(); } - +public _routeLiveEditorUpdate(name?: PropertyKey, oldValue?: unknown): boolean { + const live = routeHouseplanEditorUpdate(this.host, name, oldValue); + if (!live && this.host._resize.preview) this._resizeBaseFrameStable = false; + return live; + } +public _commitLiveEditor(): void { commitHouseplanEditor(this.host); } +public _disposeLiveEditor(): void { disposeHouseplanEditor(this.host); } +public _queuePointerMove(key: string, run: () => void): void { queueHouseplanPointerMove(this.host, key, run); } +public _flushPointerMove(key: string): void { flushHouseplanPointerMove(this.host, key); } +public _cancelPointerMove(key?: string): void { cancelHouseplanPointerMove(this.host, key); } public _help(key: Extract): TemplateResult | typeof nothing { const ariaKey = `${key}.aria` as I18nKey; const lang = langOf(this.host.hass, this.host._config?.language); @@ -2050,67 +2045,30 @@ public _limitSegmentsOf(space: any): LimitSegment[] { } public _junctionLimitViolations( - config: any, spaceId: string, + config: unknown, spaceId: string, /** #330 §4.7: an already computed masonry pass of THIS config (e.g. the * resize preflight's artifact) — the union is never paid twice. */ - sharedGeometry?: any, + sharedGeometry?: JunctionSharedGeometry | null, + roomIds?: ReadonlySet, ): JunctionLimitViolation[] { - const space = (config?.spaces || []).find((item) => item?.id === spaceId); - if (!space) return []; - const cellCm = Number(space.cell_cm) > 0 ? Number(space.cell_cm) : 5; - const segments = this._limitSegmentsOf(space); - const violations = [ - ...checkNodes(segments), - ...checkSegmentLengths(segments, cellCm, GRID_STEP_N), - ...checkNodeDistances(segments, cellCm, GRID_STEP_N), - ]; - // #330 §4.7: without the shared passes every room recomputed the FULL - // junction topology and masonry union for itself. Compute both at most - // once and hand them to every room clearance check. - let sharedNodes: ReturnType | null = null; - try { - sharedNodes = multiWallNodesForGeometry( - space.rooms || [], space.walls || [], [], GRID_STEP_N, cellCm, GRID_STEP_N, 1, - ); - } catch { sharedNodes = null; } - let sharedRoomGeometry: any = sharedGeometry?.status === 'ok' - || sharedGeometry?.status === 'degraded-extra' - ? sharedGeometry.roomGeom : null; - if (!sharedRoomGeometry && sharedNodes && sharedNodes.nodes.length) { - try { - const geometry = wallBodiesGeometry( - space.rooms || [], space.walls || [], [], [], GRID_STEP_N, cellCm, GRID_STEP_N, 1, - ); - sharedRoomGeometry = geometry?.status === 'ok' || geometry?.status === 'degraded-extra' - ? geometry.roomGeom : null; - } catch { sharedRoomGeometry = null; } - } - for (const room of (space.rooms || [])) { - const roomId = String(room?.id || ''); - if (!roomId) continue; - let inner: number[][] | null = null; - try { - inner = innerContourForRoom( - space.rooms || [], roomId, space.walls || [], [], - GRID_STEP_N, cellCm, GRID_STEP_N, 1, - sharedRoomGeometry ?? undefined, sharedNodes, - ); - } catch { inner = null; } - violations.push(...checkRoomClearance(roomId, inner, cellCm, GRID_STEP_N)); - } - return violations; + const space = ((config as { spaces?: Array<{ id?: unknown }> } | null)?.spaces || []) + .find((item) => item?.id === spaceId); + return space ? junctionLimitViolations( + config, spaceId, this._limitSegmentsOf(space), sharedGeometry, roomIds, + ) : []; } public _junctionLimitLabel(violation: JunctionLimitViolation): string { const round = (value: number) => String(Math.round(value * 10) / 10); - return this.host._t(`junction.limit_${violation.rule}` as any, { + return this.host._t(`junction.limit_${violation.rule}` as I18nKey, { actual: round(violation.actual), limit: round(violation.limit), }); } public _junctionLimitsIntroduced( - candidate: any, previousConfig: any, spaceId: string, - candidateGeometry?: unknown, + candidate: ServerConfig, previousConfig: ServerConfig, spaceId: string, + candidateGeometry?: JunctionSharedGeometry | null, + affectedRoomIds?: readonly string[], ): JunctionLimitViolation[] { // The baseline must be the previous document AS THE CANDIDATE SEES IT: a // legacy space carries no wall catalogue at all, so comparing it raw with @@ -2118,13 +2076,14 @@ public _junctionLimitsIntroduced( // refused legitimate resizes of real plans. Both sides therefore cross // the same identity barrier first. Current-version documents are already // materialized, while legacy documents cross that barrier once. + const affected = affectedRoomIds?.length ? new Set(affectedRoomIds) : undefined; let inherited: JunctionLimitViolation[] | undefined; - const previousSpace = (previousConfig?.spaces || []) - .find((space: any) => space?.id === spaceId); + const previousSpace = (previousConfig.spaces || []) + .find((space) => space?.id === spaceId); let fingerprint = ''; try { fingerprint = spacePhysicalGeometryFingerprint(previousSpace); } catch { fingerprint = ''; } - const cached = previousConfig && typeof previousConfig === 'object' + const cached = !affected && previousConfig && typeof previousConfig === 'object' ? this._junctionBaselineCache.get(previousConfig) : undefined; if (cached && fingerprint && cached.fingerprint === fingerprint && cached.spaceId === spaceId) { @@ -2135,26 +2094,34 @@ public _junctionLimitsIntroduced( const baseline = Number(previousConfig?.model_version || 0) >= WALL_SEGMENT_MODEL_VERSION ? previousConfig : commitWallSegmentModel(previousConfig).config; - inherited = this.host._junctionLimitViolations(baseline, spaceId); + inherited = this.host._junctionLimitViolations( + baseline, spaceId, affected ? null : undefined, affected, + ); } catch { // An unmigratable baseline proves nothing about inheritance; never // refuse the write on that basis. return []; } - if (previousConfig && typeof previousConfig === 'object' && fingerprint) { + if (!affected && previousConfig && typeof previousConfig === 'object' && fingerprint) { this._junctionBaselineCache.set(previousConfig, { spaceId, fingerprint, violations: inherited, }); } } let next: JunctionLimitViolation[] = []; - try { next = this.host._junctionLimitViolations(candidate, spaceId, candidateGeometry); } + let comparison = inherited; + try { + next = this.host._junctionLimitViolations(candidate, spaceId, candidateGeometry, affected); + if (affected) comparison = inherited.filter( + (item) => item.rule !== 'clearance' || affected.has(item.subject), + ); + } catch { // #331 §2.5: candidate judgment fails closed; only an unprovable // baseline above is allowed to fail open. return [{ rule: 'check_failed', subject: spaceId, actual: 0, limit: 0 }]; } - return increasedViolations(next, inherited); + return increasedViolations(next, comparison); } public _commitPhysicalGeometry( @@ -2336,14 +2303,16 @@ public _stagePointerCancel(ev: PointerEvent): void { } else if (this.host._tool === 'draw') { this._clearPlanSnapHover(); } - const viewportGestureEnded = !!this.host._pinchStart || !!this.host._panStart; this.host._pointers.delete(ev.pointerId); if (this.host._pointers.size < 2) this.host._pinchStart = null; if (this.host._pointers.size === 0) { this.host._panStart = null; this.host._panLock = null; } - if (viewportGestureEnded && this.host._pointers.size === 0) this.host.requestUpdate(); + if (this.host._viewportGestureDirty && this.host._pointers.size === 0) { + this.host._viewportGestureDirty = false; + this.host.requestUpdate(); + } } public _applyGeometryState( @@ -3296,6 +3265,11 @@ public _clampPhysicalDelta( } public _physicalMove(ev: PointerEvent): void { + ev.stopPropagation(); + queueHouseplanPointerMove(this.host, 'physical', () => this._physicalMoveNow(ev)); + } + +private _physicalMoveNow(ev: PointerEvent): void { const drag = this.host._physicalDrag; if (!drag || drag.pid !== ev.pointerId) return; ev.stopPropagation(); @@ -3317,6 +3291,7 @@ public _physicalMove(ev: PointerEvent): void { } public _physicalUp(ev: PointerEvent): void { + flushHouseplanPointerMove(this.host, 'physical'); const drag = this.host._physicalDrag; if (!drag || drag.pid !== ev.pointerId) return; ev.stopPropagation(); @@ -3385,6 +3360,8 @@ public _registerPhysicalTap( } public _cancelPhysicalGesture(): void { + cancelHouseplanPointerMove(this.host, 'physical'); + cancelHouseplanPointerMove(this.host, 'physical-rotate'); this.host._physicalDrag = null; this.host._physicalRotate = null; this.host.requestUpdate(); @@ -3406,6 +3383,12 @@ public _physicalRotateDown(ev: PointerEvent, c: WallColumnCfg): void { } public _physicalRotateMove(ev: PointerEvent): void { + ev.preventDefault(); + ev.stopPropagation(); + queueHouseplanPointerMove(this.host, 'physical-rotate', () => this._physicalRotateMoveNow(ev)); + } + +private _physicalRotateMoveNow(ev: PointerEvent): void { const drag = this.host._physicalRotate; if (!drag || drag.pid !== ev.pointerId) return; ev.preventDefault(); @@ -3418,6 +3401,7 @@ public _physicalRotateMove(ev: PointerEvent): void { } public _physicalRotateUp(ev: PointerEvent): void { + flushHouseplanPointerMove(this.host, 'physical-rotate'); const drag = this.host._physicalRotate; if (!drag || drag.pid !== ev.pointerId) return; ev.preventDefault(); @@ -3651,9 +3635,8 @@ public _rszProjectPreview( // old records as a multiset and prove only newly written/split records. // That keeps pointermove bounded by the touched thickness profile instead // of comparing every historical record with every carrier twice per frame. - const wallSignature = (wall: any): string => JSON.stringify([ - wall?.key, wall?.cm, wall?.a, wall?.b, - ]); + const wallSignature = (wall: WallEntry): string => + JSON.stringify([wall?.key, wall?.cm, wall?.a, wall?.b]); const oldWallCounts = new Map(); for (const wall of s.walls || []) { const key = wallSignature(wall); @@ -3666,13 +3649,12 @@ public _rszProjectPreview( if (remaining) oldWallCounts.set(key, remaining - 1); else changedWalls.push(wall); } - if (wallRecordCarrierViolations( - changedWalls, wallCarriers, this.host._wallKeyPitch, NORM_W, s.walls || [], - ).length) return { ok: false, reason: 'wall-metadata' }; - const preflight = this._rszSpaceCandidateGeometry(this.host._space, sp); - if (!preflight.ok) { + if (wallRecordCarrierViolations(changedWalls, wallCarriers, this.host._wallKeyPitch, + NORM_W, s.walls || []).length) return { ok: false, reason: 'wall-metadata' }; + const liveRoomIds = resizeLiveJunctionRoomIds(sp.rooms || [], changedRoomIds); + const liveSpace = resizeLiveCandidateSpace(sp, liveRoomIds); + if (!liveSpace || !this._rszSpaceCandidateGeometry(this.host._space, liveSpace).ok) return { ok: false, reason: 'physical-geometry' }; - } // #329 AC7a: a step that would ADD a junction-limit violation is never // projected, so the drag stops at the last allowed position instead of // committing an impossible plan. @@ -3682,11 +3664,22 @@ public _rszProjectPreview( (space) => (space?.id === this.host._space ? sp : space), ), }; - // #330 §4.7: reuse the masonry pass already computed by the resize - // preflight instead of rebuilding it while checking room clearance. - const limited = this._junctionLimitsIntroduced( - limitCandidate, this.host._serverCfg, this.host._space, preflight.wallGeometry, - ); + try { this._resizePreviewNodes = multiWallNodesForGeometry( + liveSpace.rooms, liveSpace.walls as unknown as WallEntry[], [], GRID_STEP_N, + this.host._cellCm, this.host._gridPitch, NORM_W, + ); + } catch { this._resizePreviewNodes = null; } + let limited: JunctionLimitViolation[]; + try { + const affected = new Set(changedRoomIds); + const next = this.host._junctionLimitViolations(limitCandidate, this.host._space, + { status: 'lightweight', multiWallNodes: this._resizePreviewNodes }, affected); + const baseline = this._resizeBaselineLimits.filter( + (item) => item.rule !== 'clearance' || affected.has(item.subject)); + limited = increasedViolations(next, baseline); + } catch { + limited = [{ rule: 'check_failed', subject: this.host._space, actual: 0, limit: 0 }]; + } if (limited.length) { this.host._rszLimitViolation = limited[0]; return { ok: false, reason: 'junction-limit' }; @@ -3696,16 +3689,16 @@ public _rszProjectPreview( value: { preview: { space: this.host._space, sp }, beforeWalls: s.walls || [], - afterWalls: sp.walls || [], - artifact: preflight.wallGeometry, + afterWalls: sp.walls || [], artifact: null, }, }; } - public _rszAcceptPreview( preview: ResizePreview | null, wallGeometry: ResizeWallArtifact | null, ): void { this.host._cfgEpoch++; + if (this.host._physicalBodiesCache) this.host._physicalBodiesCache.key = + `${this.host._space}|${this.host._cfgEpoch}|${this.host._cellCm}|${this.host._gridPitch}`; if (!preview || !wallGeometry) return; const projected = wallBodiesGeometryPath(wallGeometry); if (!projected) return; @@ -3761,6 +3754,7 @@ public _rszCandidateRenderable(preview: { space: string; sp: any } | null): bool public _rszEdgeDown(ev: PointerEvent, roomId: string, edge: number): void { if (this.host._tool !== 'resize' || this.host._resize.dragging) return; + this._resizeBaseFrameStable = true; ev.stopPropagation(); ev.preventDefault(); const rooms = this._rszRooms(); @@ -3771,6 +3765,10 @@ public _rszEdgeDown(ev: PointerEvent, roomId: string, edge: number): void { } capturePointer(ev); const plan = resolution.plan; + try { + this._resizeBaselineLimits = this.host._junctionLimitViolations( + this.host._serverCfg, this.host._space, null, new Set(plan.roomIds)); + } catch { this._resizeBaselineLimits = []; } const start = this._svgPoint(ev); const wallUnionKey = `${this.host._space}|${this.host._cfgEpoch}|${rooms.length}`; const wallUnionBefore = this.host._wallUnionCache?.key === wallUnionKey @@ -3782,6 +3780,7 @@ public _rszEdgeDown(ev: PointerEvent, roomId: string, edge: number): void { openings: this._rszOpenings(), snapshotIdentity, before: JSON.parse(snapshotIdentity) as SpaceGeometryState, wallUnionBefore, + epochBefore: this.host._cfgEpoch, }); } @@ -3801,6 +3800,11 @@ public _rszDisabledKey(ev: KeyboardEvent, reason: SafeResizeReason): void { } public _rszMove(ev: PointerEvent): void { + if (!this.host._resize.ownsPointer(ev.pointerId)) return; + ev.stopPropagation(); + queueHouseplanPointerMove(this.host, 'resize', () => this._rszMoveNow(ev)); + } +private _rszMoveNow(ev: PointerEvent): void { if (!this.host._resize.ownsPointer(ev.pointerId)) return; ev.stopPropagation(); const p = this._svgPoint(ev); @@ -3838,6 +3842,7 @@ public _rszMove(ev: PointerEvent): void { } public _rszUp(ev: PointerEvent): void { + flushHouseplanPointerMove(this.host, 'resize'); if (!this.host._resize.ownsPointer(ev.pointerId)) return; ev.stopPropagation(); const result = this.host._resize.finish({ @@ -3878,17 +3883,16 @@ public _rszUp(ev: PointerEvent): void { } public _rszCancelDrag(pointerId?: number): void { + cancelHouseplanPointerMove(this.host, 'resize'); const result = this.host._resize.cancel(this._rszSnapshot(), pointerId); if (result.kind === 'no-op') return; - // HP-1550-01/-03: a cancel just drops the overlay — the real config was - // never touched, so there is nothing to restore, no undo step and no write - this.host._cfgEpoch++; + // An identical cancel reuses the pre-drag structural caches and writes nothing. + if (result.restoreEpoch !== null) this.host._cfgEpoch = result.restoreEpoch; + else this.host._cfgEpoch++; + if (this.host._physicalBodiesCache) this.host._physicalBodiesCache.key = + `${this.host._space}|${this.host._cfgEpoch}|${this.host._cellCm}|${this.host._gridPitch}`; if (result.restoreWallUnion) { - // The server-backed geometry did not change during the gesture. The - // preview advanced the structural epoch and temporarily selected its - // candidate union, but Cancel returns to the byte-identical pre-drag - // config. Alias that already-proved result under the fresh epoch instead - // of paying another O(n log n) polygon union on the next paint. + // Alias the already-proved pre-drag union under the restored epoch. const space = this.host._spaceModel(); if (space) { const key = `${this.host._space}|${this.host._cfgEpoch}|${space.rooms.length}`; @@ -3897,6 +3901,11 @@ public _rszCancelDrag(pointerId?: number): void { this.host._wallUnionCache = entry; } } + // Retain the canonical frame only when the drag was actually painted in + // the isolated plan-editor layer. Programmatic/non-editor callers still + // need a full render to replace their preview DOM. + if (result.restoreEpoch !== null && this.host._mode === 'plan' + && this._resizeBaseFrameStable) this.host._terminalFrame = 1; this.host.requestUpdate(); } @@ -3970,7 +3979,10 @@ public _rszEdgeLabels( // masonry union + contour cache that the following render consumes; // rebuilding both independently here doubled one Resize frame. const floor = walls.length && space - ? (this.host._innerRoomContour(space, id) || poly) + ? (innerContourForRoom( + space.rooms, id, walls, this.host._openCuts(), this.host._wallKeyPitch, + this.host._cellCm, this.host._gridPitch, NORM_W, null, this._resizePreviewNodes, + ) || poly) : poly; const m2 = physical.length ? geometryArea(floorMinusBodies(floor, physical)) @@ -4033,7 +4045,9 @@ public _renderResizeMeasurements(): TemplateResult | typeof nothing { `; } -public _renderResizeLayer(view: { x: number; y: number; w: number; h: number }): TemplateResult { +public _renderResizeLayer( + view: { x: number; y: number; w: number; h: number }, roomIds?: readonly string[], + ): TemplateResult { const hr = Math.max(view.w * 0.013, 5); // finger-sized HIT radius on touch, like .vacfithandle // Wall-handle glyph: half the old circle — a wall segment with two arrows // pointing perpendicular to it (the directions the wall can be dragged). @@ -4052,7 +4066,9 @@ public _renderResizeLayer(view: { x: number; y: number; w: number; h: number }): // never once per room edge. During a drag the immutable gesture snapshot // remains authoritative inside _rszResolution. const renderSnapshot = this.host._resize.snapshotIdentity || this._rszSnapshot(); + const visibleRooms = roomIds?.length ? new Set(roomIds) : null; for (const r of rooms) { + if (visibleRooms && !visibleRooms.has(r.id)) continue; for (let i = 0; i < r.poly.length; i++) { const a = r.poly[i], b = r.poly[(i + 1) % r.poly.length]; if (Math.hypot(b[0] - a[0], b[1] - a[1]) < this.host._gridPitch) continue; @@ -4076,7 +4092,7 @@ public _renderResizeLayer(view: { x: number; y: number; w: number; h: number }): parts.push(svg``); } } - return svg`${parts}`; + return svg`${parts}`; } public _partitionOpeningCuts( @@ -4158,6 +4174,9 @@ public _replaceDecor(id: string, patch: Partial): void { } public _cancelDecorGesture(): void { + cancelHouseplanPointerMove(this.host, 'decor-transform'); + cancelHouseplanPointerMove(this.host, 'decor-move'); + cancelHouseplanPointerMove(this.host, 'backdrop'); const before = this.host._decorMove?.before || this.host._dtDrag?.before || this.host._bdDrag?.before; const sp = before && this.host._serverCfg?.spaces.find((space) => space.id === before.spaceId); if (before && sp) { @@ -4167,11 +4186,14 @@ public _cancelDecorGesture(): void { for (const key of ['plan_x', 'plan_y', 'plan_scale', 'plan_scale_x', 'plan_scale_y', 'plan_angle'] as const) delete sp[key]; Object.assign(sp, copy(before.plan_transform || {})); - this.host._cfgEpoch++; + // The restored transform is byte-identical; keep geometry caches. + this.host._decorSnapCache = null; } this.host._decorMove = null; this.host._dtDrag = null; this.host._bdDrag = null; + // Mark after named terminal setters; only the unnamed cancel frame may be retained. + if (before && sp && this.host._mode === 'decor') this.host._terminalFrame = 1; this.host.requestUpdate(); } @@ -4334,9 +4356,7 @@ public _decorShapeDown(ev: PointerEvent, shape: DecorShape): void { } public _decorMoveUpdate(ev: PointerEvent): void { - const m = this.host._decorMove!; - // Furniture is dragged by its CENTRE and has a magnet of its own - // (docs/FURNITURE.md §5) — a different rule, not a different gesture. + const m = this.host._decorMove; if (!m) return; if (m.orig?.kind === 'furniture') { this._furnMoveUpdate(ev); return; } const p = this._svgPoint(ev); const o0: any = m.orig; @@ -4374,6 +4394,11 @@ public _decorMoveUpdate(ev: PointerEvent): void { }); this.host.requestUpdate(); } +public _dmUp(): void { + flushHouseplanPointerMove(this.host, 'decor-move'); const move = this.host._decorMove; + if (move?.moved) { this._recordGeometry(this.host._t('history.decor_move'), move.before); this._saveConfig(); } + this.host._decorMove = null; this.host.requestUpdate(); + } public _decorShapeDbl(ev: MouseEvent, shape: DecorShape): void { if (this.host._mode !== 'decor' || this.host._decorTool !== 'select') return; @@ -4808,39 +4833,7 @@ public _dtUp(): void { } public _dtMeasure(): void { - const sh = this.host._dtSel; - if (!sh) { - if (this.host._dtBox) { this.host._dtBox = null; this.host.requestUpdate(); } - return; - } - let box: { id: string; x: number; y: number; w: number; h: number }; - if (sh.kind === 'line') { - const x1 = sh.x1 * NORM_W, y1 = sh.y1 * this.host._decorH; - const x2 = sh.x2 * NORM_W, y2 = sh.y2 * this.host._decorH; - box = { id: sh.id, x: Math.min(x1, x2), y: Math.min(y1, y2), - w: Math.abs(x2 - x1), h: Math.abs(y2 - y1) }; - } else if (sh.kind === 'furniture' || sh.kind === 'image' - || sh.kind === 'rect' || sh.kind === 'ellipse') { - // …and furniture needs no measuring at all: its box IS the config. The - // frame therefore appears in the SAME frame as the selection, not one - // after it, and a resize can never be a render behind the shape. - box = { id: sh.id, x: sh.x * NORM_W, y: sh.y * this.host._decorH, - w: sh.w * NORM_W, h: sh.h * this.host._decorH }; - } else { - const el = this.host.renderRoot.querySelector(`text.dtext[data-id="${sh.id}"]`) as SVGGraphicsElement | null; - if (!el || typeof (el as any).getBBox !== 'function') return; - let b: DOMRect; - try { b = el.getBBox(); } catch { return; } // not rendered yet (hidden card) - if (!b || (!b.width && !b.height)) return; - box = { id: sh.id, x: b.x, y: b.y, w: b.width, h: b.height }; - } - const cur = this.host._dtBox; - const same = cur && cur.id === box.id && Math.abs(cur.x - box.x) < 0.01 - && Math.abs(cur.y - box.y) < 0.01 && Math.abs(cur.w - box.w) < 0.01 - && Math.abs(cur.h - box.h) < 0.01; - if (same) return; - this.host._dtBox = box; - this.host.requestUpdate(); + measureHouseplanDecorText(this.host); } public _deleteDecor(id: string): void { @@ -5068,7 +5061,7 @@ public _furnPointerUp(ev: PointerEvent): boolean { } public _furnMoveUpdate(ev: PointerEvent): void { - const m = this.host._decorMove!; + const m = this.host._decorMove; if (!m) return; if (m.orig.kind !== 'furniture') return; const o: any = m.orig; const sp = this.host._curSpaceCfg; @@ -5273,6 +5266,10 @@ public _bdStart(ev: PointerEvent, corner?: number[], rotate = false): boolean { } public _bdMove(ev: PointerEvent): void { + queueHouseplanPointerMove(this.host, 'backdrop', () => this._bdMoveNow(ev)); + } + +private _bdMoveNow(ev: PointerEvent): void { const d = this.host._bdDrag; if (!d) return; const p = this._svgPoint(ev); @@ -5329,6 +5326,7 @@ public _bdReset(): void { } public _bdUp(): void { + flushHouseplanPointerMove(this.host, 'backdrop'); const d = this.host._bdDrag; this.host._bdDrag = null; if (d?.moved) { @@ -6433,6 +6431,10 @@ public _opPointerDown(ev: PointerEvent, o: OpeningCfg): void { } public _opPointerMove(ev: PointerEvent, o: OpeningCfg): void { + queueHouseplanPointerMove(this.host, 'opening', () => this._opPointerMoveNow(ev, o)); + } + +private _opPointerMoveNow(ev: PointerEvent, o: OpeningCfg): void { if (!this.host._opDrag || this.host._opDrag.id !== o.id) return; // audit L4: the other drag pipelines require 3 px before calling it a drag. // Without it every tap counted as a drag: the properties dialog never @@ -6552,6 +6554,7 @@ public _opRuler( public _opPointerUp(ev: PointerEvent, o: OpeningCfg): void { if (!this.host._opDrag || this.host._opDrag.id !== o.id) return; + flushHouseplanPointerMove(this.host, 'opening'); const drag = this.host._opDrag; const moved = drag.moved; this.host._opMeasure = null; // badges and the center tick live only through the drag @@ -6560,8 +6563,8 @@ public _opPointerUp(ev: PointerEvent, o: OpeningCfg): void { this._commitPhysicalGeometry(this.host._t('history.move_opening'), drag.before); } // keep the flag until the click event that follows pointerup, then let it go - if (moved) window.setTimeout(() => (this.host._opDrag = null), 0); - else this.host._opDrag = null; + const finish = () => { this.host._opDrag = null; this.host.requestUpdate(); }; + if (moved) window.setTimeout(finish, 0); else finish(); } public _opClick(ev: MouseEvent, o: RenderOpening): void { @@ -6893,13 +6896,11 @@ public _markupMove(ev: MouseEvent): void { conflicts: resolved.conflicts, }; this._syncPlanSnapConflictMarkers(resolved.conflicts); + this._syncPlanSnapActiveMarker(resolved.candidate); // Before the first click there is no rubber-band to repaint. Updating the // dedicated marker avoids walking the complete large-house Lit tree for // every mouse move while click still resolves from the event coordinate. - if (!this.host._path.length) { - this._syncPlanSnapActiveMarker(resolved.candidate); - return; - } + if (!this.host._path.length) return; this.host._cursorPt = resolved.point; return; } diff --git a/src/houseplan-render-lifecycle.ts b/src/houseplan-render-lifecycle.ts new file mode 100644 index 00000000..e48e592a --- /dev/null +++ b/src/houseplan-render-lifecycle.ts @@ -0,0 +1,99 @@ +import { + haRegistryDiagnostics, + type HaBindingStatus, +} from './ha-binding-status'; +import type { HassRenderDependencies, HassRenderSnapshot } from './render-invalidation'; + +export interface HouseplanDiagnosticsCore { + registry: ReturnType; + bindings: Record; +} + +type DiagnosticMarker = { removed?: boolean; binding: string }; + +/** State kept outside the already oversized card element (#34). */ +export class RenderLifecycle { + private seen: unknown = null; + private diagnosticsCache: HouseplanDiagnosticsCore | null = null; + + observe( + before: HassRenderSnapshot | null | undefined, + after: HassRenderSnapshot | null | undefined, + dependencies: HassRenderDependencies | null, + intake: () => void, + ): void { + this.invalidateDiagnosticsForHass(before, after, dependencies); + this.intake(after, intake); + } + + intake(snapshot: unknown, run: () => void): void { + if (!snapshot || this.seen === snapshot) return; + this.seen = snapshot; + run(); + } + + invalidate(): void { + this.diagnosticsCache = null; + } + + private invalidateDiagnosticsForHass( + before: HassRenderSnapshot | null | undefined, + after: HassRenderSnapshot | null | undefined, + dependencies: HassRenderDependencies | null, + ): void { + if (!this.diagnosticsCache) return; + if (before?.entities !== after?.entities || before?.devices !== after?.devices + || [...(dependencies?.entityIds || [])].some((entityId) => + !!before?.states?.[entityId] !== !!after?.states?.[entityId])) { + this.invalidate(); + } + } + + diagnostics( + hass: unknown, + markers: readonly DiagnosticMarker[], + resolve: (binding: string) => HaBindingStatus, + ): HouseplanDiagnosticsCore { + if (this.diagnosticsCache) return this.diagnosticsCache; + const bindings: Record = { + active: 0, ha_disabled: 0, orphaned: 0, unverified: 0, + }; + for (const marker of markers) { + if (marker.removed || marker.binding === 'virtual') continue; + bindings[resolve(marker.binding).kind]++; + } + this.diagnosticsCache = { registry: haRegistryDiagnostics(hass), bindings }; + return this.diagnosticsCache; + } +} + +/** Operational work that must survive a deliberately skipped visual update. */ +interface HassIntakeHost { + _hassSequence: number; + _renderSnapshotAt: number; + _continuity: { note: (reason: string) => void }; + _ensureHaRegistryAuthority: () => void; + _planHassMemo: unknown; + _hookConnection: () => void; + _loadOk: boolean; + _loading: boolean; + _loadTries: number; + _loadFromServer: () => unknown; + _maybeRebuildDevices: () => void; + _vacTick: () => void; + _activityTick: () => void; +} + +export function intakeHass(value: object): void { + const host = value as HassIntakeHost; + host._hassSequence++; + host._renderSnapshotAt = Date.now(); + host._continuity.note('hass-snapshot'); + host._ensureHaRegistryAuthority(); + host._planHassMemo = null; + host._hookConnection(); + if (!host._loadOk && !host._loading && host._loadTries < 8) host._loadFromServer(); + host._maybeRebuildDevices(); + host._vacTick(); + host._activityTick(); +} diff --git a/src/interaction-types.ts b/src/interaction-types.ts new file mode 100644 index 00000000..3baf6605 --- /dev/null +++ b/src/interaction-types.ts @@ -0,0 +1,38 @@ +import type { DevicePlacement } from './device-position-history'; +import type { OpeningDimension } from './opening-dimensions'; +import type { OpeningPlacementCore } from './opening-placement'; +import type { OpeningFaceOffset } from './opening-symbol-placement'; +import type { ResolvedPartitionOpening, PartitionOpeningOrphanReason } from './partition-openings'; +import type { OpeningCfg } from './types'; + +/** Opening rulers shared by the card shell and lazy editor runtime. */ +export interface OpMeasure { + labels: Array<{ x: number; y: number; text: string; dimension?: OpeningDimension }>; + guide: { x: number; y: number; angle: number } | null; +} + +export type OpeningPlacementCandidate = Omit & { + face: OpeningFaceOffset; + measure: OpMeasure; +}; + +export type RenderOpening = OpeningCfg & { + rx: number; ry: number; rlen: number; + partitionHost?: ResolvedPartitionOpening; + orphanReason?: PartitionOpeningOrphanReason; +}; + +export interface DeviceDragState { + id: string; + spaceId: string; + displayName: string; + pointerId: number; + source: Element | null; + sx: number; + sy: number; + ox: number; + oy: number; + moved: boolean; + before: DevicePlacement | null; + start: DevicePlacement; +} diff --git a/src/junction-limits.ts b/src/junction-limits.ts index 62df5951..200b9fb8 100644 --- a/src/junction-limits.ts +++ b/src/junction-limits.ts @@ -10,7 +10,26 @@ * * Thresholds are absolute (centimetres, degrees) and do not scale with the * space's `cell_cm` (spec r1-L1). - */ +*/ + +import { GRID_STEP_N } from './space-geometry'; +import { + innerContourForRoom, multiWallNodesForGeometry, wallBodiesGeometry, + type MultiWallNodeMap, type WallBodiesGeometryResult, type WallEntry, +} from './wall-thickness'; +import type { RoomCfg } from './types'; + +interface JunctionLimitSpace { + id?: string; + cell_cm?: unknown; + rooms?: RoomCfg[]; + walls?: WallEntry[]; +} + +type JunctionLimitConfig = { spaces?: JunctionLimitSpace[] }; +export type JunctionSharedGeometry = + | Pick + | { status: 'lightweight'; multiWallNodes?: MultiWallNodeMap | null }; export const MIN_JUNCTION_ANGLE_DEG = 15; export const MAX_JUNCTION_VALENCE = 6; @@ -41,6 +60,60 @@ export interface LimitSegment { cm?: number; } +/** Shared full/affected-room validation used by editor writes and lightweight resize previews. */ +export function junctionLimitViolations( + config: unknown, + spaceId: string, + segments: readonly LimitSegment[], + sharedGeometry?: JunctionSharedGeometry | null, + roomIds?: ReadonlySet, +): JunctionLimitViolation[] { + const spaces = (config as JunctionLimitConfig | null)?.spaces || []; + const space = spaces.find((item) => item?.id === spaceId); + if (!space) return []; + const cellCm = Number(space.cell_cm) > 0 ? Number(space.cell_cm) : 5; + const violations = [ + ...checkNodes(segments), ...checkSegmentLengths(segments, cellCm, GRID_STEP_N), + ...checkNodeDistances(segments, cellCm, GRID_STEP_N), + ]; + let nodes: ReturnType | null = + sharedGeometry?.multiWallNodes || null; + if (!nodes) { + try { + nodes = multiWallNodesForGeometry( + space.rooms || [], space.walls || [], [], GRID_STEP_N, cellCm, GRID_STEP_N, 1, + ); + } catch { nodes = null; } + } + const completeGeometry = sharedGeometry && sharedGeometry.status !== 'lightweight' + ? sharedGeometry : null; + let roomGeometry: unknown = completeGeometry?.status === 'ok' + || completeGeometry?.status === 'degraded-extra' ? completeGeometry.roomGeom : null; + const lightweight = sharedGeometry === null || sharedGeometry?.status === 'lightweight'; + if (!roomGeometry && !lightweight && nodes?.nodes.length) { + try { + const geometry = wallBodiesGeometry( + space.rooms || [], space.walls || [], [], [], GRID_STEP_N, cellCm, GRID_STEP_N, 1, + ); + roomGeometry = geometry?.status === 'ok' || geometry?.status === 'degraded-extra' + ? geometry.roomGeom : null; + } catch { roomGeometry = null; } + } + for (const room of space.rooms || []) { + const roomId = String(room?.id || ''); + if (!roomId || (roomIds && !roomIds.has(roomId))) continue; + let inner: number[][] | null = null; + try { + inner = innerContourForRoom( + space.rooms || [], roomId, space.walls || [], [], GRID_STEP_N, cellCm, GRID_STEP_N, 1, + lightweight ? null : roomGeometry ?? undefined, nodes, + ); + } catch { inner = null; } + violations.push(...checkRoomClearance(roomId, inner, cellCm, GRID_STEP_N)); + } + return violations; +} + const EPS = 1e-9; /** * #331 §2.1: below this two points are ONE node / a node is ON the wall — diff --git a/src/live-editor.ts b/src/live-editor.ts new file mode 100644 index 00000000..9e5dc700 --- /dev/null +++ b/src/live-editor.ts @@ -0,0 +1,411 @@ +import { nothing, render, svg, type TemplateResult } from 'lit'; +import { cancelHouseplanPointerMove } from './pointer-move-queue'; +import { gridVisualUnits } from './grid-scale'; +import type { RenderOpening } from './interaction-types'; +import type { SpaceDisplay } from './logic'; +import type { SpaceModel, WallEntry } from './types'; +import { + wallBodyNeedsSolid, wallCmToUnits, wallEdgePathD, + wallHatchNeedsSolid, wallHatchStepUnits, + type WallEdgeBody, +} from './wall-thickness'; +import { NORM_W } from './space-geometry'; + +interface LiveEditorState { + raf: number; + hidden: HTMLElement[]; + transparent: HTMLElement[]; + dimmed: HTMLElement[]; +} + +interface LiveEditorHost { + isConnected: boolean; + renderRoot: ParentNode; + requestUpdate: () => void; + _mode: 'view' | 'plan' | 'devices' | 'decor'; + _tool: string; + _deviceDrag: { id: string } | null; + _physicalDrag?: unknown; + _physicalRotate?: unknown; + _decorDraft?: unknown; + _decorMove?: { id: string } | null; + _dtDrag?: { id: string } | null; + _bdDrag?: unknown; + _opDrag?: unknown; + _resize?: { dragging: boolean; plan?: { roomIds: string[]; movingOpeningIds: string[] } | null }; + _layout: unknown; + _dtBox: { id: string; x: number; y: number; w: number; h: number } | null; + _dtSel: { id: string; kind: string } | null; + _opMeasureView: { guide: unknown } | null; + _devices: { id: string }[]; + _liveEditorPaintCount: number; + _baseVb: () => number[]; + _viewOr: (viewBox: number[]) => { x: number; y: number; w: number; h: number }; + _spaceDisplayForRender: () => SpaceDisplay; + _renderWallBodies: (display: unknown) => unknown; + _renderResizeMeasurements: () => unknown; + _renderOpenings: (display: unknown, onlyIds?: readonly string[]) => unknown; + _renderResizeLayer: (view: unknown, roomIds?: readonly string[]) => unknown; + _renderMarkupLayer: (viewBox: number[]) => unknown; + _renderHiddenWallDiagnosticOverlay: () => unknown; + _renderOpeningPlacementPreview: () => unknown; + _renderOpeningDimensionGuides: (measure: unknown) => unknown; + _renderOpeningCenterTick: (guide: unknown) => unknown; + _renderActiveChainInk: () => unknown; + _renderPlanSnapOverlay: () => unknown; + _renderWallThickUi: () => unknown; + _renderDecorLayer: (onlyId?: string | null) => unknown; + _renderBackdropFrame: (view: unknown) => unknown; + _renderTextFrame: (view: unknown) => unknown; + _renderLiveEditorMeasurements: (view: unknown) => unknown; + _livePos: (device: { id: string }) => { x: number; y: number }; + _scenePoint: (point: number[]) => number[]; + _renderProjection: string; + _spaceModel: () => SpaceModel | null; + _spaceWalls: WallEntry[]; + _cellCm: number; + _gridPitch: number; + _openingsR: RenderOpening[]; + _stageEl: HTMLElement | null; + _fillColors: { wall_fill: { c: string; a: number } }; +} + +const states = new WeakMap(); + +const liveProperties = new Set([ + '_cursorPt', '_opMeasure', '_physicalDrag', '_physicalRotate', + '_decorDraft', '_dtBox', '_dtDrag', '_bdDrag', '_furnPreviewInput', '_layout', +]); +const hoverProperties = new Set(['_cursorPt', '_opMeasure', '_furnPreviewInput']); +const gestureProperties = new Set([ + '_physicalDrag', '_physicalRotate', '_decorDraft', '_dtDrag', '_bdDrag', +]); + +const stateOf = (host: object): LiveEditorState => { + let state = states.get(host); + if (!state) { + state = { raf: 0, hidden: [], transparent: [], dimmed: [] }; + states.set(host, state); + } + return state; +}; + +const activeEditorGesture = (host: LiveEditorHost): boolean => host._mode !== 'view' && ( + !!host._deviceDrag || !!host._physicalDrag || !!host._physicalRotate + || !!host._decorDraft || !!host._decorMove || !!host._dtDrag || !!host._bdDrag + || !!host._opDrag + || !!host._resize?.dragging +); + +/** + * ReactiveElement calls `requestUpdate(name, oldValue)` from generated state + * setters. Only pointer-owned values are diverted; selection, dialogs, toasts + * and terminal null assignments still receive a normal host update. + */ +export function routeHouseplanEditorUpdate( + value: object, + name?: PropertyKey, + oldValue?: unknown, +): boolean { + const host = value as LiveEditorHost; + if (!host?.isConnected || host._mode === 'view') return false; + const namedLive = name !== undefined && liveProperties.has(name); + if (name === '_layout' && !host._deviceDrag) return false; + if (name === '_dtBox' && !host._dtDrag) return false; + if (name !== undefined && gestureProperties.has(name) + && oldValue == null + && (host as unknown as Record)[name] != null + && activeEditorGesture(host)) { + // Pointerdown may change selection/chrome once. Subsequent moves stay live. + return false; + } + if (name !== undefined && gestureProperties.has(name) && host[name] == null) return false; + const route = (namedLive && (hoverProperties.has(name!) || activeEditorGesture(host))) + || (name === undefined && activeEditorGesture(host)); + if (!route) return false; + scheduleHouseplanEditor(host); + return true; +} + +const hide = (state: LiveEditorState, root: ParentNode, selector: string): void => { + for (const element of root.querySelectorAll(selector)) { + if (element.closest('[data-hp-live-editor]')) continue; + if (element.style.visibility === 'hidden') continue; + element.style.visibility = 'hidden'; + state.hidden.push(element); + } +}; + +/** Keep the captured source hittable while only its live copy stays visible. */ +const makeTransparent = (state: LiveEditorState, root: ParentNode, selector: string): void => { + for (const element of root.querySelectorAll(selector)) { + if (element.closest('[data-hp-live-editor]')) continue; + if (element.style.opacity === '0') continue; + element.style.opacity = '0'; + state.transparent.push(element); + } +}; + +const restore = (state: LiveEditorState): void => { + for (const element of state.hidden) element.style.removeProperty('visibility'); + state.hidden.length = 0; + for (const element of state.transparent) element.style.removeProperty('opacity'); + state.transparent.length = 0; + for (const element of state.dimmed) element.style.removeProperty('opacity'); + state.dimmed.length = 0; +}; + +/** Resize moves paint individual wall strips; pointerup still validates the canonical union. */ +const resizePreviewWalls = ( + host: LiveEditorHost, display: SpaceDisplay, roomIds: readonly string[] = [], +): TemplateResult => { + if (host._renderProjection === 'iso') return svg``; + const space = host._spaceModel(); + if (!space) return svg``; + const affected = new Set(roomIds); + const boundary = (space.rooms || []).filter((room) => !!room.id && affected.has(room.id)) + .flatMap((room) => { + const poly = room.poly || []; + return poly.map((point: number[], index: number) => [ + point, poly[(index + 1) % poly.length], + ]); + }); + const onSegment = (point: number[], segment: number[][]): boolean => { + const [a, b] = segment; + const dx = b[0] - a[0], dy = b[1] - a[1]; + const length2 = dx * dx + dy * dy; + if (!(length2 > 0)) return false; + const t = ((point[0] - a[0]) * dx + (point[1] - a[1]) * dy) / length2; + const clamped = Math.max(0, Math.min(1, t)); + return Math.hypot(point[0] - a[0] - dx * clamped, point[1] - a[1] - dy * clamped) < 1e-4; + }; + const edge = (a: number[], b: number[], cm: number, key: string): WallEdgeBody | null => { + const length = Math.hypot(b[0] - a[0], b[1] - a[1]); + if (!(length > 1e-9) || !(cm > 0)) return null; + const half = wallCmToUnits(cm, host._cellCm, host._gridPitch) / 2; + const nx = -(b[1] - a[1]) / length * half; + const ny = (b[0] - a[0]) / length * half; + return { + key, kind: 'outer', cm, a, b, depthUnits: half * 2, + quad: [[a[0] + nx, a[1] + ny], [b[0] + nx, b[1] + ny], + [b[0] - nx, b[1] - ny], [a[0] - nx, a[1] - ny]], + }; + }; + const edges = [ + ...host._spaceWalls.filter((wall) => { + const a = (wall.a || []).map((value: number) => value * NORM_W); + const b = (wall.b || []).map((value: number) => value * NORM_W); + return boundary.some((segment: number[][]) => onSegment(a, segment) && onSegment(b, segment)); + }).map((wall, index) => edge( + (wall.a || []).map((value: number) => value * NORM_W), + (wall.b || []).map((value: number) => value * NORM_W), + wall.cm, wall.key || `wall-${index}`, + )), + ].filter((item): item is WallEdgeBody => !!item); + const openings = host._openingsR.map((opening) => ({ + x: opening.rx, y: opening.ry, angle: opening.angle, length: opening.rlen, + })); + const paths = edges.map((edge) => wallEdgePathD(edge, openings)).filter(Boolean); + if (!paths.length) return svg``; + const depth = Math.max(...edges.map((edge) => edge.depthUnits), 0); + const view = host._viewOr(host._baseVb()); + const px = host._stageEl?.clientWidth && view.w ? host._stageEl.clientWidth / view.w : 1; + const solid = wallBodyNeedsSolid(depth, px) + || wallHatchNeedsSolid(wallHatchStepUnits(host._cellCm), px); + const stroke = display?.color || '#607d8b'; + const fill = host._fillColors.wall_fill; + const d = paths.join(' '); + return svg` + + + `; +}; + +const planTemplate = (host: LiveEditorHost): TemplateResult => { + const vb = host._baseVb(); + const view = host._viewOr(vb); + const measure = host._opMeasureView; + if (host._resize?.dragging) { + const display = host._spaceDisplayForRender(); + const plan = host._resize.plan; + return svg``; + } + const display = host._spaceDisplayForRender(); + return svg``; +}; + +const editorTemplate = (host: LiveEditorHost): TemplateResult | typeof nothing => { + if (host._mode === 'plan') return planTemplate(host); + if (host._mode === 'decor') { + const view = host._viewOr(host._baseVb()); + const activeId = host._dtDrag?.id || host._decorMove?.id || null; + return svg``; + } + return nothing; +}; + +const paintDevice = (host: LiveEditorHost, root: ParentNode): void => { + const drag = host._deviceDrag; + if (!drag) return; + const device = host._devices.find((candidate) => candidate.id === drag.id); + const element = [...root.querySelectorAll('[data-hp="device"]')] + .find((candidate) => candidate.dataset.id === drag.id); + if (!device || !element) return; + const position = host._livePos(device); + const point = host._scenePoint([position.x, position.y]); + const view = host._viewOr(host._baseVb()); + element.style.left = `${((point[0] - view.x) / view.w) * 100}%`; + element.style.top = `${((point[1] - view.y) / view.h) * 100}%`; +}; + +const makeDecorShapeTransparent = ( + state: LiveEditorState, root: ParentNode, id: string, +): void => { + for (const element of root.querySelectorAll('.decorlayer [data-hp="decor"]')) { + if (element.closest('[data-hp-live-editor]') || element.dataset.id !== id) continue; + if (element.style.opacity === '0') continue; + element.style.opacity = '0'; + state.transparent.push(element); + } +}; + +const makeMovingOpeningsTransparent = ( + state: LiveEditorState, root: ParentNode, ids: readonly string[], +): void => { + const moving = new Set(ids); + for (const element of root.querySelectorAll('.openinglayer [data-hp="opening"]')) { + if (!element.closest('[data-hp-live-editor]') && moving.has(element.dataset.id || '')) { + element.style.opacity = '0'; + state.transparent.push(element); + } + } +}; + +const dim = (state: LiveEditorState, root: ParentNode, selector: string): void => { + for (const element of root.querySelectorAll(selector)) { + if (element.closest('[data-hp-live-editor]')) continue; + element.style.opacity = '0.35'; + state.dimmed.push(element); + } +}; + +/** One editor-only render root per card; the settled scene remains untouched. */ +export function paintHouseplanEditor(value: object): void { + const host = value as LiveEditorHost; + const root = host.renderRoot; + if (!root) return; + const state = stateOf(host); + restore(state); + paintDevice(host, root); + const target = root.querySelector('[data-hp-live-editor]'); + if (!target) return; + if (host._mode === 'plan') { + makeTransparent(state, root, '.hp-editor-only-layer:not(.hp-plan-snap-layer)'); + if (host._opDrag) hide(state, root, '.wallbodies'); + if (host._resize?.dragging) { + dim(state, root, '.wallbodies'); + makeTransparent(state, root, '.resize-layer'); + makeMovingOpeningsTransparent(state, root, host._resize.plan?.movingOpeningIds || []); + } + if (host._opDrag) makeTransparent(state, root, '.openinglayer'); + } else if (host._mode === 'decor') { + const activeId = host._dtDrag?.id || host._decorMove?.id; + if (activeId) makeDecorShapeTransparent(state, root, activeId); + makeTransparent(state, root, '.dtframe, .backdropframe'); + } + render(editorTemplate(host), target); + const htmlTarget = root.querySelector('[data-hp-live-editor-html]'); + if (htmlTarget) { + const view = host._viewOr(host._baseVb()); + render(host._renderLiveEditorMeasurements(view), htmlTarget); + } + host._liveEditorPaintCount = (host._liveEditorPaintCount || 0) + 1; +} + +export function scheduleHouseplanEditor(value: object): void { + const state = stateOf(value); + if (state.raf || typeof requestAnimationFrame !== 'function') return; + state.raf = requestAnimationFrame(() => { + state.raf = 0; + paintHouseplanEditor(value); + }); +} + +/** Only text needs a DOM measurement; every box-like item owns its config box. */ +export function measureHouseplanDecorText(value: object): void { + const host = value as LiveEditorHost; + const shape = host._dtSel; + if (!shape) { + if (host._dtBox) { host._dtBox = null; host.requestUpdate(); } + return; + } + if (shape.kind !== 'text') return; + const element = host.renderRoot.querySelector( + `text.dtext[data-id="${shape.id}"]`, + ) as SVGGraphicsElement | null; + if (!element || typeof element.getBBox !== 'function') return; + let measured: DOMRect; + try { measured = element.getBBox(); } catch { return; } + if (!measured || (!measured.width && !measured.height)) return; + const box = { + id: shape.id, x: measured.x, y: measured.y, + w: measured.width, h: measured.height, + }; + const current = host._dtBox; + if (current?.id === box.id && Math.abs(current.x - box.x) < 0.01 + && Math.abs(current.y - box.y) < 0.01 && Math.abs(current.w - box.w) < 0.01 + && Math.abs(current.h - box.h) < 0.01) return; + host._dtBox = box; + host.requestUpdate(); +} + +export function commitHouseplanEditor(value: object): void { + const host = value as LiveEditorHost; + const state = stateOf(value); + if (state.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(state.raf); + state.raf = 0; + restore(state); + const target = (host.renderRoot as ParentNode | undefined) + ?.querySelector('[data-hp-live-editor]'); + if (target) render(nothing, target); + const htmlTarget = (host.renderRoot as ParentNode | undefined) + ?.querySelector('[data-hp-live-editor-html]'); + if (htmlTarget) { + const view = host._viewOr(host._baseVb()); + render(host._renderLiveEditorMeasurements(view), htmlTarget); + } +} + +export function disposeHouseplanEditor(host: object): void { + cancelHouseplanPointerMove(host); + const state = states.get(host); + if (!state) return; + if (state.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(state.raf); + restore(state); + states.delete(host); +} diff --git a/src/live-hover.ts b/src/live-hover.ts new file mode 100644 index 00000000..2dddf69e --- /dev/null +++ b/src/live-hover.ts @@ -0,0 +1,96 @@ +import { lqiColor } from './logic'; + +export interface LiveTip { + x: number; + y: number; + title: string; + meta: string; + lqi?: number | null; + temp?: number | null; + hum?: number | null; + room?: boolean; +} + +interface HoverState { + room: unknown; + space: string; +} + +interface LiveHoverHost { + renderRoot: ParentNode; + _tip: LiveTip | null; + _t: (key: 'tip.temp_avg' | 'tip.hum_avg' | 'tip.lqi') => string; + _hoverRoom: { space: string; room: unknown } | null; + _spaceModel: () => unknown; + _roomHoverPaths: (model: unknown) => { fillD: string; outlineD: string } | null; +} + +const states = new WeakMap(); + +const appendMeta = (tip: HTMLElement, label: string, value?: string, color?: string): void => { + if (!value) return; + const row = document.createElement('span'); + row.className = 'm'; + if (!label) row.textContent = value; + else { + row.append(`${label} `); + const strong = document.createElement('b'); + strong.textContent = value; + if (color) strong.style.color = color; + row.append(strong); + } + tip.append(row); +}; + +const syncTip = (host: LiveHoverHost, root: ParentNode): void => { + const element = root.querySelector('[data-hp-live-tip]'); + if (!element) return; + const tip = host._tip as LiveTip | null; + if (!tip) { + element.hidden = true; + element.replaceChildren(); + return; + } + element.style.left = `${tip.x + 12}px`; + element.style.top = `${tip.y + 12}px`; + element.replaceChildren(); + const title = document.createElement('b'); + title.textContent = tip.title; + element.append(title); + appendMeta(element, '', tip.meta); + appendMeta(element, host._t('tip.temp_avg'), tip.temp == null ? '' : `${tip.temp}°`); + appendMeta(element, host._t('tip.hum_avg'), tip.hum == null ? '' : `${tip.hum}%`); + appendMeta(element, host._t('tip.lqi'), tip.lqi == null ? '' : String(tip.lqi), + tip.lqi == null ? undefined : lqiColor(tip.lqi)); + element.hidden = false; +}; + +const setRoomPath = (root: ParentNode, selector: string, d: string): void => { + const path = root.querySelector(selector); + if (!path) return; + path.setAttribute('d', d); + path.toggleAttribute('hidden', !d); +}; + +/** Update the ordinary mouse hover without scheduling a full card render. */ +export function syncHouseplanHover(value: object): void { + const host = value as LiveHoverHost; + const root = host.renderRoot; + if (!root) return; + syncTip(host, root); + const hover = host._hoverRoom; + const previous = states.get(value); + const space = hover?.space || ''; + const room = hover?.room || null; + if (previous?.room === room && previous?.space === space) return; + states.set(value, { room, space }); + const model = hover && host._spaceModel(); + const paths = model ? host._roomHoverPaths(model) : null; + setRoomPath(root, '[data-hp-live-room-hover="fill"]', paths?.fillD || ''); + setRoomPath(root, '[data-hp-live-room-hover="halo"]', paths?.outlineD || ''); + setRoomPath(root, '[data-hp-live-room-hover="outline"]', paths?.outlineD || ''); +} + +export function resetHouseplanHover(host: object): void { + states.delete(host); +} diff --git a/src/live-interaction-runtime.ts b/src/live-interaction-runtime.ts new file mode 100644 index 00000000..7686918a --- /dev/null +++ b/src/live-interaction-runtime.ts @@ -0,0 +1,72 @@ +import { + commitHouseplanViewport, disposeHouseplanViewport, scheduleHouseplanViewport, +} from './live-viewport'; +import { resetHouseplanHover, syncHouseplanHover } from './live-hover'; +import { + classifyHassRenderChange, type HassRenderDependencies, type HassRenderSnapshot, +} from './render-invalidation'; +import type { RenderLifecycle } from './houseplan-render-lifecycle'; + +interface LiveRuntimeHost { + _renderLife: RenderLifecycle; + _pointers: { size: number }; + _cameraTransition: { active: boolean }; + _deviceDrag?: unknown; + _physicalDrag?: unknown; + _physicalRotate?: unknown; + _decorMove?: unknown; + _decorDraft?: unknown; + _dtDrag?: unknown; + _bdDrag?: unknown; + _opDrag?: unknown; + _resize?: { dragging: boolean }; +} + +/** Lazily loaded DOM-only fast paths; the card keeps a full-render fallback. */ +export class LiveRuntime { + private deferredHass = false; + public constructor(private readonly host: object) {} + public hass( + before: HassRenderSnapshot | null | undefined, + after: HassRenderSnapshot | null | undefined, + dependencies: HassRenderDependencies | null, + intake: () => void, + ): boolean { + const change = classifyHassRenderChange(before, after, dependencies); + const defer = change === 'state' && this.active(); + if (defer) this.deferredHass = true; + const render = change !== 'none' && !defer; + // Preserve ReactiveElement's ordering for snapshots which are about to + // render: willUpdate owns their intake. Tests, config editors and HA can + // legitimately finish other same-turn mutations after assigning `hass`. + // Only a deliberately skipped update needs operational intake here. + if (!render) { + (this.host as LiveRuntimeHost)._renderLife.observe(before, after, dependencies, intake); + } + return render; + } + public clear(): void { this.deferredHass = false; } + public take(): boolean { + const pending = this.deferredHass; + this.deferredHass = false; + return pending; + } + public viewport(now = false): void { scheduleHouseplanViewport(this.host, now); } + public hover(): void { syncHouseplanHover(this.host); } + public active(): boolean { + const host = this.host as LiveRuntimeHost; + return host._pointers.size > 0 || host._cameraTransition.active || !!host._deviceDrag + || !!host._physicalDrag || !!host._physicalRotate || !!host._decorMove + || !!host._decorDraft || !!host._dtDrag || !!host._bdDrag || !!host._opDrag + || !!host._resize?.dragging; + } + public commit(): void { + commitHouseplanViewport(this.host); + resetHouseplanHover(this.host); + syncHouseplanHover(this.host); + } + public dispose(): void { + disposeHouseplanViewport(this.host); + resetHouseplanHover(this.host); + } +} diff --git a/src/live-viewport.ts b/src/live-viewport.ts new file mode 100644 index 00000000..066a84b3 --- /dev/null +++ b/src/live-viewport.ts @@ -0,0 +1,165 @@ +export interface LiveViewBox { + x: number; + y: number; + w: number; + h: number; +} + +interface LiveViewportFrame { + view: LiveViewBox; + floor: LiveViewBox; + zoom: number; +} + +interface LiveViewportState { + painted: LiveViewportFrame | null; + pending: LiveViewportFrame | null; + raf: number; +} + +interface LiveViewportHost { + _viewOr: (viewBox: number[]) => LiveViewBox; + _baseVb: () => number[]; + _floorView: (view: LiveViewBox) => LiveViewBox; + _zoom: number; + renderRoot: ParentNode; +} + +const states = new WeakMap(); +const stateOf = (host: object): LiveViewportState => { + let state = states.get(host); + if (!state) { + state = { painted: null, pending: null, raf: 0 }; + states.set(host, state); + } + return state; +}; + +export const liveViewBoxText = (view: LiveViewBox): string => + `${view.x} ${view.y} ${view.w} ${view.h}`; + +export interface LiveLayerProjection { + translateXPercent: number; + translateYPercent: number; + scaleX: number; + scaleY: number; +} + +/** Map an HTML percentage layer painted for `before` onto `after`. */ +export function liveLayerProjection( + before: LiveViewBox, + after: LiveViewBox, +): LiveLayerProjection { + return { + translateXPercent: ((before.x - after.x) / after.w) * 100, + translateYPercent: ((before.y - after.y) / after.h) * 100, + scaleX: before.w / after.w, + scaleY: before.h / after.h, + }; +} + +export const isIdentityLiveLayerProjection = (projection: LiveLayerProjection): boolean => + projection.translateXPercent === 0 + && projection.translateYPercent === 0 + && projection.scaleX === 1 + && projection.scaleY === 1; + +const finiteView = (view: LiveViewBox): boolean => + [view.x, view.y, view.w, view.h].every(Number.isFinite) && view.w > 0 && view.h > 0; + +const setLayerProjection = ( + layer: HTMLElement, + projection: LiveLayerProjection | null, +): void => { + if (!projection) { + layer.style.removeProperty('transform'); + layer.style.removeProperty('transform-origin'); + layer.style.removeProperty('will-change'); + return; + } + layer.style.transformOrigin = '0 0'; + layer.style.willChange = 'transform'; + layer.style.transform = `translate(${projection.translateXPercent}%,${projection.translateYPercent}%) scale(${projection.scaleX},${projection.scaleY})`; +}; + +/** One atomic DOM-only viewport paint; no Lit host update is involved. */ +export function paintLiveViewport( + root: ParentNode, + painted: LiveViewportFrame, + current: LiveViewportFrame, +): void { + if (!finiteView(painted.view) || !finiteView(current.view) || !finiteView(current.floor)) return; + const viewBox = liveViewBoxText(current.view); + const floorBox = liveViewBoxText(current.floor); + for (const svg of root.querySelectorAll('[data-hp-live-viewbox="camera"]')) { + svg.setAttribute('viewBox', viewBox); + } + for (const svg of root.querySelectorAll('[data-hp-live-viewbox="floor"]')) { + svg.setAttribute('viewBox', floorBox); + } + const projection = liveLayerProjection(painted.view, current.view); + for (const layer of root.querySelectorAll('[data-hp-live-layer="camera"]')) { + // Keeping an identity transform after terminal reconciliation changes the + // browser compositing path and therefore the settled raster by a few colour + // levels. The fast path must leave byte-equivalent DOM/CSS when it is idle. + setLayerProjection(layer, isIdentityLiveLayerProjection(projection) ? null : projection); + } + const badge = root.querySelector('[data-hp-live-zoom]'); + if (badge) { + badge.hidden = current.zoom <= 1; + const value = badge.querySelector('[data-hp-live-zoom-value]'); + if (value) value.textContent = `${Math.round(current.zoom * 100)}%`; + } +} + +const frameOf = (host: LiveViewportHost): LiveViewportFrame => { + const view = host._viewOr(host._baseVb()); + return { view: { ...view }, floor: { ...host._floorView(view) }, zoom: host._zoom }; +}; + +/** Coalesce camera input into one lightweight paint per animation frame. */ +export function scheduleHouseplanViewport(value: object, now = false): void { + const host = value as LiveViewportHost; + const state = stateOf(value); + const next = frameOf(host); + if (now) { + if (state.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(state.raf); + state.raf = 0; state.pending = null; + if (!state.painted) state.painted = next; + paintLiveViewport(host.renderRoot, state.painted, next); + return; + } + state.pending = next; + if (state.raf || typeof requestAnimationFrame !== 'function') return; + state.raf = requestAnimationFrame(() => { + state.raf = 0; + const next = state.pending; + state.pending = null; + const root = host.renderRoot as ParentNode | undefined; + if (!next || !root) return; + if (!state.painted) state.painted = next; + paintLiveViewport(root, state.painted, next); + }); +} + +/** Record a complete Lit frame and remove any temporary HTML projection. */ +export function commitHouseplanViewport(value: object): void { + const host = value as LiveViewportHost; + const state = stateOf(value); + if (state.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(state.raf); + state.raf = 0; + state.pending = null; + state.painted = frameOf(host); + const root = host.renderRoot as ParentNode | undefined; + if (!root) return; + for (const layer of root.querySelectorAll('[data-hp-live-layer="camera"]')) { + setLayerProjection(layer, null); + } + paintLiveViewport(root, state.painted, state.painted); +} + +export function disposeHouseplanViewport(host: object): void { + const state = states.get(host); + if (state?.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(state.raf); + states.delete(host); +} diff --git a/src/pointer-move-queue.ts b/src/pointer-move-queue.ts new file mode 100644 index 00000000..90f28627 --- /dev/null +++ b/src/pointer-move-queue.ts @@ -0,0 +1,47 @@ +interface QueuedMove { run: () => void } + +const moveQueues = new WeakMap>(); + +/** + * Keep only the latest raw move from one browser event turn. + * + * Gesture state must be current before a following pointerup/cancel is handled: + * postponing the input itself until requestAnimationFrame lets the terminal + * event clear that state first. The live renderer still coalesces the paint + * to one animation frame; this queue only coalesces the state calculation. + */ +export function queueHouseplanPointerMove(host: object, key: string, run: () => void): void { + let queues = moveQueues.get(host); + if (!queues) { + queues = new Map(); + moveQueues.set(host, queues); + } + const queued = queues.get(key); + if (queued) { + queued.run = run; + return; + } + const entry: QueuedMove = { run }; + queues.set(key, entry); + queueMicrotask(() => { + if (queues!.get(key) !== entry) return; + queues!.delete(key); + entry.run(); + }); +} + +export function flushHouseplanPointerMove(host: object, key: string): void { + const queues = moveQueues.get(host); + const queued = queues?.get(key); + if (!queued) return; + queues!.delete(key); + queued.run(); +} + +export function cancelHouseplanPointerMove(host: object, key?: string): void { + const queues = moveQueues.get(host); + if (!queues) return; + const entries = key ? [...queues].filter(([name]) => name === key) : [...queues]; + for (const [name] of entries) queues.delete(name); + if (!queues.size) moveQueues.delete(host); +} diff --git a/src/render-device-snapshot.ts b/src/render-device-snapshot.ts index ff79a832..78078c8b 100644 --- a/src/render-device-snapshot.ts +++ b/src/render-device-snapshot.ts @@ -22,6 +22,8 @@ export interface RenderDeviceSnapshot { readonly presentations: ReadonlyMap; /** Extra resolved render facts (for example a vacuum puck/trail frame). */ readonly facts: ReadonlyMap; + /** Exact state rows whose identity may invalidate the painted frame. */ + readonly entityIds: readonly string[]; } /** @@ -148,6 +150,7 @@ export function createRenderDeviceSnapshot(options: { [key, cloneFact(presentation)] as const)), facts: readonlyMap([...(options.facts || [])].map(([key, fact]) => [key, cloneFact(fact)] as const)), + entityIds: Object.freeze([...entityIds]), }); } diff --git a/src/render-invalidation.ts b/src/render-invalidation.ts new file mode 100644 index 00000000..7ce87373 --- /dev/null +++ b/src/render-invalidation.ts @@ -0,0 +1,76 @@ +/** HA data that can change the painted House Plan frame. */ +export interface HassRenderDependencies { + readonly entityIds: Iterable; +} + +export type HassRenderChange = 'none' | 'state' | 'structural'; + +export interface HassRenderSnapshot { + readonly states?: Readonly>; + readonly connection?: unknown; + readonly entities?: unknown; + readonly devices?: unknown; + readonly areas?: unknown; + readonly themes?: unknown; + readonly user?: unknown; + readonly config?: unknown; + readonly floors?: unknown; + readonly services?: unknown; + readonly panels?: unknown; + readonly language?: unknown; + readonly locale?: Readonly> | null; + readonly [key: string]: unknown; +} + +/** + * Classify one HA assignment without walking the complete `hass.states` map. + * + * Home Assistant keeps unchanged state rows by identity. Comparing only the + * dependency rows therefore catches state, attribute and availability changes + * while making an unrelated tick O(number of plan dependencies), not + * O(number of HA entities). Structural values deliberately fail open: they are + * rare and can alter formatting, registry resolution, permissions or actions. + */ +export function classifyHassRenderChange( + before: HassRenderSnapshot | null | undefined, + after: HassRenderSnapshot | null | undefined, + dependencies: HassRenderDependencies | null, +): HassRenderChange { + if (before === after) return 'none'; + if (!before || !after || !dependencies) return 'structural'; + + if (before.connection !== after.connection + || before.entities !== after.entities + || before.devices !== after.devices + || before.areas !== after.areas + || before.themes !== after.themes + || before.user !== after.user + || before.config !== after.config + || before.floors !== after.floors + || before.services !== after.services + || before.panels !== after.panels + || before.language !== after.language + || before.locale?.language !== after.locale?.language + || before.locale?.number_format !== after.locale?.number_format + || before.locale?.time_format !== after.locale?.time_format + || before.locale?.date_format !== after.locale?.date_format) { + return 'structural'; + } + + // `states` is the only top-level collection for which this classifier can + // prove irrelevance from the dependency projection. A newly introduced HA + // capability must therefore fail open until it receives an explicit rule; + // otherwise a frontend release could silently leave that UI stale. + const compared = new Set([ + 'states', 'connection', 'entities', 'devices', 'areas', 'themes', 'user', + 'config', 'floors', 'services', 'panels', 'language', 'locale', + ]); + for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) { + if (!compared.has(key) && before[key] !== after[key]) return 'structural'; + } + + for (const entityId of dependencies.entityIds) { + if (before.states?.[entityId] !== after.states?.[entityId]) return 'state'; + } + return 'none'; +} diff --git a/src/resize-controller.ts b/src/resize-controller.ts index fca71f4e..b4aedfba 100644 --- a/src/resize-controller.ts +++ b/src/resize-controller.ts @@ -40,7 +40,7 @@ export type ResizeFinishOutcome = | { kind: 'no-op' }; export type ResizeCancelOutcome = - | { kind: 'cancelled'; restoreWallUnion: TWallUnion | null } + | { kind: 'cancelled'; restoreWallUnion: TWallUnion | null; restoreEpoch: number | null } | { kind: 'no-op' }; export interface ResizeBeginInput { @@ -54,6 +54,7 @@ export interface ResizeBeginInput { snapshotIdentity: string; before: TBefore; wallUnionBefore: TWallUnion | null; + epochBefore: number; } interface AcceptedPreview { @@ -79,6 +80,7 @@ interface ResizeSession { changedRoomIds: string[]; rejectionNotified: boolean; wallUnionBefore: TWallUnion | null; + epochBefore: number; accepted: AcceptedPreview | null; } @@ -149,6 +151,7 @@ export class ResizeController changedRoomIds: [...input.plan.roomIds], rejectionNotified: false, wallUnionBefore: input.wallUnionBefore, + epochBefore: input.epochBefore, accepted: null, }; return true; @@ -273,10 +276,11 @@ export class ResizeController } this._session = null; this._eligibility = null; + const snapshotMatches = currentSnapshotIdentity === session.snapshotIdentity; return { kind: 'cancelled', - restoreWallUnion: currentSnapshotIdentity === session.snapshotIdentity - ? session.wallUnionBefore : null, + restoreWallUnion: snapshotMatches ? session.wallUnionBefore : null, + restoreEpoch: snapshotMatches ? session.epochBefore : null, }; } diff --git a/src/resize-live-preflight.ts b/src/resize-live-preflight.ts new file mode 100644 index 00000000..4516fc10 --- /dev/null +++ b/src/resize-live-preflight.ts @@ -0,0 +1,210 @@ +import { GRID_STEP_N } from './space-geometry'; + +type DataRecord = Record; + +export interface ResizeLiveBounds { + minX: number; + minY: number; + maxX: number; + maxY: number; +} + +const asRecord = (value: unknown): DataRecord | null => + typeof value === 'object' && value !== null ? value as DataRecord : null; + +const recordsOf = (value: unknown): DataRecord[] => + Array.isArray(value) ? value.flatMap((item) => { + const record = asRecord(item); + return record ? [record] : []; + }) : []; + +const pointOf = (value: unknown): number[] | null => { + if (!Array.isArray(value)) return null; + const x = Number(value[0]), y = Number(value[1]); + return Number.isFinite(x) && Number.isFinite(y) ? [x, y] : null; +}; + +const pointsOf = (value: unknown): number[][] => + Array.isArray(value) ? value.flatMap((item) => { + const point = pointOf(item); + return point ? [point] : []; + }) : []; + +function roomPoints(room: DataRecord): number[][] { + const poly = pointsOf(room.poly); + if (poly.length >= 3) return poly; + const x = Number(room.x), y = Number(room.y); + const w = Number(room.w), h = Number(room.h); + if (![x, y, w, h].every(Number.isFinite)) return []; + return [[x, y], [x + w, y], [x + w, y + h], [x, y + h]]; +} + +function boundsOf(points: readonly number[][]): ResizeLiveBounds | null { + if (!points.length) return null; + const xs = points.map((point) => point[0]); + const ys = points.map((point) => point[1]); + return { + minX: Math.min(...xs), minY: Math.min(...ys), + maxX: Math.max(...xs), maxY: Math.max(...ys), + }; +} + +/** + * Live validation follows the resize solver's affected-room identity instead + * of the size of the floor. Cross-room safety is still checked against the + * complete space before commit; the pointer loop proves the moving rooms and + * their nearby independent physical bodies without rebuilding remote rooms. + */ +export function resizeLiveRoomIds( + roomsInput: readonly unknown[], changedRoomIds: readonly string[], +): string[] { + const rooms = recordsOf(roomsInput); + const changed = new Set(changedRoomIds); + return rooms.flatMap((room) => { + const id = String(room.id || ''); + return id && changed.has(id) ? [id] : []; + }); +} + +/** One non-transitive neighbour layer is enough to retain junction rays. */ +export function resizeLiveJunctionRoomIds( + roomsInput: readonly unknown[], changedRoomIds: readonly string[], epsilon = 1e-8, +): string[] { + const rooms = recordsOf(roomsInput); + const changed = new Set(changedRoomIds); + const changedBounds = rooms.flatMap((room) => { + if (!changed.has(String(room.id || ''))) return []; + const bounds = boundsOf(roomPoints(room)); + return bounds ? [bounds] : []; + }); + return rooms.flatMap((room) => { + const id = String(room.id || ''); + const bounds = boundsOf(roomPoints(room)); + const touches = !!bounds && changedBounds.some((candidate) => + bounds.minX <= candidate.maxX + epsilon && bounds.maxX + epsilon >= candidate.minX + && bounds.minY <= candidate.maxY + epsilon && bounds.maxY + epsilon >= candidate.minY); + return id && (changed.has(id) || touches) ? [id] : []; + }); +} + +/** Build the exact local physical component checked on every live resize frame. */ +export function resizeLiveCandidateSpace( + spaceInput: unknown, changedRoomIds: readonly string[], +): (DataRecord & { rooms: DataRecord[]; walls: DataRecord[] }) | null { + const space = asRecord(spaceInput); + if (!space) return null; + const allRooms = recordsOf(space.rooms); + const ids = new Set(resizeLiveRoomIds(allRooms, changedRoomIds)); + const rooms = allRooms.filter((room) => ids.has(String(room.id || ''))); + const points = rooms.flatMap(roomPoints); + const core = boundsOf(points); + if (!core || !rooms.length) return null; + const rawCellCm = Number(space.cell_cm); + const cellCm = Number.isFinite(rawCellCm) && rawCellCm > 0 ? rawCellCm : 5; + const roomEdges = rooms.flatMap((room) => { + const poly = roomPoints(room); + return poly.map((point, index) => [point, poly[(index + 1) % poly.length]]); + }); + const edgeEpsilon = GRID_STEP_N * 0.02; + const distanceToSegment = (point: number[], a: number[], b: number[]): number => { + const dx = b[0] - a[0], dy = b[1] - a[1]; + const denominator = dx * dx + dy * dy; + const t = denominator > 0 ? Math.max(0, Math.min(1, + ((point[0] - a[0]) * dx + (point[1] - a[1]) * dy) / denominator)) : 0; + return Math.hypot(point[0] - a[0] - t * dx, point[1] - a[1] - t * dy); + }; + const segmentDistance = (a: number[], b: number[], c: number[], d: number[]): number => { + const cross = (p: number[], q: number[], r: number[]) => + (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]); + const abC = cross(a, b, c), abD = cross(a, b, d); + const cdA = cross(c, d, a), cdB = cross(c, d, b); + if (abC * abD <= edgeEpsilon * edgeEpsilon + && cdA * cdB <= edgeEpsilon * edgeEpsilon + && Math.min(a[0], b[0]) <= Math.max(c[0], d[0]) + edgeEpsilon + && Math.min(c[0], d[0]) <= Math.max(a[0], b[0]) + edgeEpsilon + && Math.min(a[1], b[1]) <= Math.max(c[1], d[1]) + edgeEpsilon + && Math.min(c[1], d[1]) <= Math.max(a[1], b[1]) + edgeEpsilon) return 0; + return Math.min( + distanceToSegment(a, c, d), distanceToSegment(b, c, d), + distanceToSegment(c, a, b), distanceToSegment(d, a, b), + ); + }; + const boundaryDistance = (a: number[], b: number[]): number => Math.min( + ...roomEdges.map(([c, d]) => segmentDistance(a, b, c, d)), + ); + const overlapsRoomBoundary = (a: number[], b: number[]): boolean => + roomEdges.some(([c, d]) => { + const dx = d[0] - c[0], dy = d[1] - c[1]; + const length = Math.hypot(dx, dy); + if (!(length > edgeEpsilon)) return false; + const lineDistance = (point: number[]) => Math.abs( + (point[0] - c[0]) * dy - (point[1] - c[1]) * dx) / length; + if (lineDistance(a) > edgeEpsilon || lineDistance(b) > edgeEpsilon) return false; + const project = (point: number[]) => ( + (point[0] - c[0]) * dx + (point[1] - c[1]) * dy) / (length * length); + const ta = project(a), tb = project(b); + return Math.min(1, Math.max(ta, tb)) - Math.max(0, Math.min(ta, tb)) > edgeEpsilon; + }); + const wallOverlaps = (item: DataRecord): boolean => { + const a = pointOf(item.a), b = pointOf(item.b); + if (a && b) return overlapsRoomBoundary(a, b); + const [midpoint] = String(item.key || '').split('@'); + const coordinates = midpoint?.split(',').map(Number) || []; + if (coordinates.length === 2 && coordinates.every(Number.isFinite)) { + return roomEdges.some(([start, end]) => distanceToSegment(coordinates, start, end) + <= edgeEpsilon); + } + // Unknown legacy data remains in the proof so malformed local records + // cannot be hidden merely because their position cannot be established. + return true; + }; + const walls = recordsOf(space.walls).filter(wallOverlaps); + const wallSegments = recordsOf(space.wall_segments).filter(wallOverlaps); + const roomWallHalf = Math.max(0, ...[...walls, ...wallSegments] + .map((item) => Number(item.cm)).filter(Number.isFinite)) / cellCm * GRID_STEP_N / 2; + const partitions = recordsOf(space.partitions).filter((item) => { + const a = pointOf(item.a), b = pointOf(item.b); + if (!a || !b) return true; + const half = Math.max(0, Number(item.cm) || 0) / cellCm * GRID_STEP_N / 2; + return boundaryDistance(a, b) <= roomWallHalf + half + edgeEpsilon; + }); + const roomDrafts = recordsOf(space.room_drafts).filter((item) => { + const draftPoints = pointsOf(item.points); + if (draftPoints.length < 2) return true; + const segments = recordsOf(item.segments); + return draftPoints.some((point, index) => { + if (index + 1 >= draftPoints.length) return false; + const half = Math.max(0, Number(segments[index]?.cm) || 0) + / cellCm * GRID_STEP_N / 2; + return boundaryDistance(point, draftPoints[index + 1]) + <= roomWallHalf + half + edgeEpsilon; + }); + }); + const wallColumns = recordsOf(space.wall_columns).filter((item) => { + const center = pointOf(item.center); + if (!center) return true; + const radius = Math.max(0, Number(item.cm) || 0) / cellCm * GRID_STEP_N; + return roomEdges.some(([start, end]) => distanceToSegment(center, start, end) + <= roomWallHalf + radius + edgeEpsilon); + }); + const partitionIds = new Set(partitions.map((item) => String(item.id || '')).filter(Boolean)); + const openings = recordsOf(space.openings).filter((item) => { + const host = asRecord(item.host); + if (host?.kind === 'partition') return partitionIds.has(String(host.id || '')); + const center = pointOf([item.x, item.y]); + if (!center) return true; + return roomEdges.some(([start, end]) => distanceToSegment(center, start, end) + <= edgeEpsilon); + }); + return { + ...space, + rooms, + walls, + wall_segments: wallSegments, + open_spans: recordsOf(space.open_spans).filter(wallOverlaps), + partitions, + room_drafts: roomDrafts, + wall_columns: wallColumns, + openings, + }; +} diff --git a/src/zigbee-topology-overlay-bridge.ts b/src/zigbee-topology-overlay-bridge.ts index 705c6b34..a0f65ef4 100644 --- a/src/zigbee-topology-overlay-bridge.ts +++ b/src/zigbee-topology-overlay-bridge.ts @@ -17,6 +17,6 @@ export function renderZigbeeTopologyOverlay(input: { if (!input.view || input.kiosk || input.hass?.user?.is_admin !== true || !zigbeeTopologySettingsOf(input.settings).enabled) return nothing; void import('./hp-zigbee-topology-overlay'); - return html``; } diff --git a/test/houseplan-render-lifecycle.test.mjs b/test/houseplan-render-lifecycle.test.mjs new file mode 100644 index 00000000..24a71e65 --- /dev/null +++ b/test/houseplan-render-lifecycle.test.mjs @@ -0,0 +1,93 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +import { RenderLifecycle } from '../test-build/houseplan-render-lifecycle.js'; +import { LiveRuntime } from '../test-build/live-interaction-runtime.js'; + +const row = (state = 'off') => ({ entity_id: 'light.plan', state, attributes: {} }); +const base = () => { + const connection = {}; + const entities = {}; + const devices = {}; + const areas = {}; + const themes = {}; + const user = {}; + const config = {}; + const floors = {}; + const services = {}; + const panels = {}; + const locale = { language: 'en', number_format: 'language', time_format: 'language' }; + return { + connection, entities, devices, areas, themes, user, config, floors, services, panels, locale, + language: 'en', states: { 'light.plan': row(), 'sensor.other': { state: '0' } }, + }; +}; + +test('HA intake runs once even when an unrelated visual update is skipped', () => { + const lifecycle = new RenderLifecycle(); + const before = base(); + const after = { ...before, states: { ...before.states, 'sensor.other': { state: '1' } } }; + let intakes = 0; + lifecycle.observe(before, after, { entityIds: ['light.plan'] }, () => intakes++); + lifecycle.intake(after, () => intakes++); + assert.equal(intakes, 1); +}); + +const liveHost = (lifecycle) => ({ + _renderLife: lifecycle, _pointers: new Map(), _cameraTransition: { active: false }, + _deviceDrag: null, _physicalDrag: null, _physicalRotate: null, _decorMove: null, + _decorDraft: null, _dtDrag: null, _bdDrag: null, _opDrag: null, + _resize: { dragging: false }, +}); + +test('a relevant HA tick is deferred only while a continuous interaction is active', () => { + const lifecycle = new RenderLifecycle(); + const host = liveHost(lifecycle); + const live = new LiveRuntime(host); + const before = base(); + const after = { ...before, states: { ...before.states, 'light.plan': row('on') } }; + host._pointers.set(1, {}); + assert.equal(live.hass(before, after, { entityIds: ['light.plan'] }, () => {}), false); + assert.equal(live.take(), true); + assert.equal(live.take(), false); + host._pointers.clear(); + const final = { ...after, states: { ...after.states, 'light.plan': row('off') } }; + assert.equal(live.hass(after, final, { entityIds: ['light.plan'] }, () => {}), true); +}); + +test('gesture reconciliation is last-wins and a normal full update subsumes it', () => { + const lifecycle = new RenderLifecycle(); + const host = liveHost(lifecycle); + const live = new LiveRuntime(host); + const before = base(); + const first = { ...before, states: { ...before.states, 'light.plan': row('on') } }; + const second = { ...first, states: { ...first.states, 'light.plan': row('unavailable') } }; + host._pointers.set(1, {}); + assert.equal(live.hass(before, first, { entityIds: ['light.plan'] }, () => {}), false); + assert.equal(live.hass(first, second, { entityIds: ['light.plan'] }, () => {}), false); + assert.equal(live.take(), true); + assert.equal(live.take(), false); + + live.hass(second, first, { entityIds: ['light.plan'] }, () => {}); + live.clear(); + assert.equal(live.take(), false); +}); + +test('diagnostics scan is cached and invalidated by tracked state presence', () => { + const lifecycle = new RenderLifecycle(); + const before = base(); + const markers = [{ binding: 'device:kitchen' }]; + let scans = 0; + const resolve = () => { + scans++; + return { kind: 'active', enabledEntityIds: ['light.plan'], allEntityIds: ['light.plan'] }; + }; + assert.equal(lifecycle.diagnostics(before, markers, resolve).bindings.active, 1); + assert.equal(lifecycle.diagnostics(before, markers, resolve).bindings.active, 1); + assert.equal(scans, 1); + + const missing = { ...before, states: { 'sensor.other': before.states['sensor.other'] } }; + lifecycle.observe(before, missing, { entityIds: ['light.plan'] }, () => {}); + lifecycle.diagnostics(missing, markers, resolve); + assert.equal(scans, 2); +}); diff --git a/test/live-editor.test.mjs b/test/live-editor.test.mjs new file mode 100644 index 00000000..3b3d870d --- /dev/null +++ b/test/live-editor.test.mjs @@ -0,0 +1,77 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { + disposeHouseplanEditor, + routeHouseplanEditorUpdate, +} from '../test-build/live-editor.js'; +import { + cancelHouseplanPointerMove, + flushHouseplanPointerMove, + queueHouseplanPointerMove, +} from '../test-build/pointer-move-queue.js'; + +test('editor live routing keeps pointerdown and terminal changes reactive', () => { + const beforeRaf = globalThis.requestAnimationFrame; + const beforeCancel = globalThis.cancelAnimationFrame; + globalThis.requestAnimationFrame = () => 17; + globalThis.cancelAnimationFrame = () => {}; + const host = { + isConnected: true, + _mode: 'plan', + _physicalDrag: { moved: false }, + }; + try { + assert.equal(routeHouseplanEditorUpdate(host, '_physicalDrag', null), false); + assert.equal(routeHouseplanEditorUpdate(host, '_physicalDrag', { moved: false }), true); + host._physicalDrag = null; + assert.equal(routeHouseplanEditorUpdate(host, '_physicalDrag', { moved: true }), false); + assert.equal(routeHouseplanEditorUpdate(host, '_toast', ''), false); + } finally { + disposeHouseplanEditor(host); + globalThis.requestAnimationFrame = beforeRaf; + globalThis.cancelAnimationFrame = beforeCancel; + } +}); + +test('pointer-following editor hover routes while View remains reactive', () => { + const beforeRaf = globalThis.requestAnimationFrame; + const beforeCancel = globalThis.cancelAnimationFrame; + globalThis.requestAnimationFrame = () => 21; + globalThis.cancelAnimationFrame = () => {}; + const host = { isConnected: true, _mode: 'plan' }; + try { + assert.equal(routeHouseplanEditorUpdate(host, '_cursorPt', null), true); + host._mode = 'view'; + assert.equal(routeHouseplanEditorUpdate(host, '_cursorPt', [1, 2]), false); + } finally { + disposeHouseplanEditor(host); + globalThis.requestAnimationFrame = beforeRaf; + globalThis.cancelAnimationFrame = beforeCancel; + } +}); + +test('pointer move queue is event-turn coalesced, last-wins and flushable', async () => { + const host = {}; + const seen = []; + try { + queueHouseplanPointerMove(host, 'drag', () => seen.push(1)); + queueHouseplanPointerMove(host, 'drag', () => seen.push(2)); + assert.deepEqual(seen, []); + await Promise.resolve(); + assert.deepEqual(seen, [2]); + + queueHouseplanPointerMove(host, 'drag', () => seen.push(3)); + flushHouseplanPointerMove(host, 'drag'); + assert.deepEqual(seen, [2, 3]); + await Promise.resolve(); + assert.deepEqual(seen, [2, 3]); + + queueHouseplanPointerMove(host, 'drag', () => seen.push(4)); + cancelHouseplanPointerMove(host, 'drag'); + await Promise.resolve(); + assert.deepEqual(seen, [2, 3]); + } finally { + disposeHouseplanEditor(host); + } +}); diff --git a/test/live-viewport.test.mjs b/test/live-viewport.test.mjs new file mode 100644 index 00000000..a732f817 --- /dev/null +++ b/test/live-viewport.test.mjs @@ -0,0 +1,31 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +import { + isIdentityLiveLayerProjection, + liveLayerProjection, + liveViewBoxText, +} from '../test-build/live-viewport.js'; + +test('live viewport projects pan and zoom from the last complete frame', () => { + assert.deepEqual( + liveLayerProjection( + { x: 0, y: 0, w: 1000, h: 500 }, + { x: 100, y: 50, w: 500, h: 250 }, + ), + { translateXPercent: -20, translateYPercent: -20, scaleX: 2, scaleY: 2 }, + ); +}); + +test('live viewport serializes one exact SVG camera box', () => { + assert.equal(liveViewBoxText({ x: -12.5, y: 4, w: 800, h: 450 }), '-12.5 4 800 450'); +}); + +test('a settled live viewport removes its compositor transform', () => { + const settled = { x: 17.25, y: -4, w: 800, h: 450 }; + assert.equal(isIdentityLiveLayerProjection(liveLayerProjection(settled, settled)), true); + assert.equal(isIdentityLiveLayerProjection(liveLayerProjection( + settled, + { ...settled, x: settled.x + 0.001 }, + )), false); +}); diff --git a/test/performance-budget.test.mjs b/test/performance-budget.test.mjs index ed7ba2d4..98b94dea 100644 --- a/test/performance-budget.test.mjs +++ b/test/performance-budget.test.mjs @@ -111,3 +111,28 @@ test('absolute performance smoke needs no baseline and enforces hard ceilings', assert.equal(rejected.pass, false); assert.ok(rejected.failures.some((check) => check.id === 'timing.firstStableRenderMs.median')); }); + +test('named interaction windows enforce their own absolute Long Task limits', () => { + const windowBudgets = { + ...budgets, + longTaskWindows: { + editorSeries: { maxSingleMs: 150, maxCountP95: 3, maxTotalP95Ms: 300 }, + }, + }; + const candidate = report(); + candidate.rows[0].longTasks.editorSeries = { + supported: true, count: 3, maxMs: 149, totalMs: 299, + }; + candidate.rows[1].longTasks.editorSeries = { + supported: true, count: 4, maxMs: 151, totalMs: 301, + }; + const result = evaluatePerformanceBudget({ baseline: report(), candidate, budgets: windowBudgets }); + assert.deepEqual( + new Set(result.failures.map((check) => check.id)), + new Set([ + 'longTask.editorSeries.maxSingleMs', + 'longTask.editorSeries.countP95', + 'longTask.editorSeries.totalP95Ms', + ]), + ); +}); diff --git a/test/performance-contract.test.mjs b/test/performance-contract.test.mjs index c1b19a6b..f0461e84 100644 --- a/test/performance-contract.test.mjs +++ b/test/performance-contract.test.mjs @@ -70,7 +70,7 @@ test('performance contracts reference real production members', () => { const source = readHouseplanProductionSource(); for (const contract of [LARGE_HOUSE_CARD_CONTRACT, GLOW_CARD_CONTRACT]) { for (const name of currentProductionMembers(contract)) { - assert.match(source, new RegExp(`\\b(?:private\\s+(?:declare\\s+|get\\s+)?|get\\s+)${name}\\b`), + assert.match(source, new RegExp(`\\b(?:private\\s+(?:(?:declare|readonly|get)\\s+)*|get\\s+)${name}\\b`), `${contract.label} declares missing production member ${name}`); } } @@ -90,10 +90,16 @@ test('large-house contract accepts only an explicit current or stable resize own ); Object.assign(fields, { _booting: false, + _bootSoft: false, + _cameraTransition: {}, _cleanFloorCache: new Map(), + _continuity: {}, + _decorList: [], + _decorTool: 'select', _devices: [], _glowClipCache: new Map(), _gridPitch: 1, + _hassSequence: 0, _loadOk: true, _model: [], _path: [], diff --git a/test/performance-workflow.test.mjs b/test/performance-workflow.test.mjs index 20ed0cfd..0cf45da5 100644 --- a/test/performance-workflow.test.mjs +++ b/test/performance-workflow.test.mjs @@ -33,6 +33,7 @@ test('full performance is isolated to stable, scheduled and manual entry points' '- large-house', '- isometric', '- plan-snap', + '- interaction', '- blend', '- overlay', '- space-default', @@ -43,7 +44,7 @@ test('full performance is isolated to stable, scheduled and manual entry points' ]) assert.ok(workflow.includes(contract), `missing full-gate contract: ${contract}`); assert.ok(workflow.includes('if [ -f baseline/scripts/bundle-sync.mjs ]; then')); - assert.equal((workflow.match(/--samples=7 --warmups=1/g) || []).length, 14); + assert.equal((workflow.match(/--samples=7 --warmups=1/g) || []).length, 16); const release = readWorkflow('release.yml'); assert.ok(release.includes('if: ${{ !github.event.release.prerelease }}')); diff --git a/test/render-device-snapshot.test.mjs b/test/render-device-snapshot.test.mjs index 1f7fb3e3..3ba343cd 100644 --- a/test/render-device-snapshot.test.mjs +++ b/test/render-device-snapshot.test.mjs @@ -58,9 +58,11 @@ test('RenderDeviceSnapshot keeps immutable facts and excludes live HA capabiliti assert.deepEqual(snapshot.positions.get('one'), { x: 12, y: 34 }); assert.equal(snapshot.presentations.get(presentationSnapshotKey('one', true)).visual.status, 'working'); assert.equal(snapshot.facts.get('vacuum:one').moving, true); + assert.deepEqual([...snapshot.entityIds], ['light.one']); assert.equal('set' in snapshot.positions, false); assert.equal('set' in snapshot.presentations, false); assert.equal('set' in snapshot.facts, false); + assert.equal('add' in snapshot.entityIds, false); }); test('RenderDeviceSnapshot exposes one immutable vacuum-only roster subset', () => { diff --git a/test/render-invalidation.test.mjs b/test/render-invalidation.test.mjs new file mode 100644 index 00000000..e6c59e10 --- /dev/null +++ b/test/render-invalidation.test.mjs @@ -0,0 +1,63 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +import { classifyHassRenderChange } from '../test-build/render-invalidation.js'; + +const entity = (id, state = 'off') => ({ entity_id: id, state, attributes: {} }); + +const base = () => { + const connection = {}; + const entities = {}; + const devices = {}; + const areas = {}; + const themes = {}; + const user = {}; + const config = {}; + const floors = {}; + const services = {}; + const panels = {}; + const locale = { language: 'en', number_format: 'language', time_format: 'language' }; + return { + connection, entities, devices, areas, themes, user, config, floors, services, panels, locale, + language: 'en', + states: { 'light.plan': entity('light.plan'), 'sensor.other': entity('sensor.other') }, + }; +}; + +const deps = { entityIds: new Set(['light.plan']) }; + +test('an unrelated HA state row does not invalidate the plan frame', () => { + const before = base(); + const after = { + ...before, + states: { ...before.states, 'sensor.other': entity('sensor.other', 'on') }, + }; + assert.equal(classifyHassRenderChange(before, after, deps), 'none'); +}); + +test('a dependency row identity or presence change invalidates state', () => { + const before = base(); + const changed = { + ...before, + states: { ...before.states, 'light.plan': entity('light.plan', 'on') }, + }; + assert.equal(classifyHassRenderChange(before, changed, deps), 'state'); + + const missing = { ...before, states: { 'sensor.other': before.states['sensor.other'] } }; + assert.equal(classifyHassRenderChange(before, missing, deps), 'state'); +}); + +test('structural HA changes fail open while same-object assignment is free', () => { + const before = base(); + assert.equal(classifyHassRenderChange(before, before, deps), 'none'); + assert.equal(classifyHassRenderChange(before, { ...before, themes: {} }, deps), 'structural'); + assert.equal(classifyHassRenderChange(before, { ...before, entities: {} }, deps), 'structural'); + assert.equal(classifyHassRenderChange(before, { ...before, locale: { ...before.locale, language: 'ru' } }, deps), 'structural'); + assert.equal(classifyHassRenderChange(before, { ...before, future_capability: {} }, deps), 'structural'); +}); + +test('missing dependency authority fails open', () => { + const before = base(); + assert.equal(classifyHassRenderChange(before, { ...before, states: { ...before.states } }, null), 'structural'); + assert.equal(classifyHassRenderChange(null, before, deps), 'structural'); +}); diff --git a/test/resize-controller.test.mjs b/test/resize-controller.test.mjs index 5baa4db7..101c771e 100644 --- a/test/resize-controller.test.mjs +++ b/test/resize-controller.test.mjs @@ -1,7 +1,12 @@ import test from 'node:test'; import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; import { ResizeController } from '../test-build/resize-controller.js'; +import { + resizeLiveCandidateSpace, resizeLiveJunctionRoomIds, resizeLiveRoomIds, +} from '../test-build/resize-live-preflight.js'; +import { checkSpacePhysicalGeometry } from '../test-build/plan-geometry-preflight.js'; import { resolveSafeResize } from '../test-build/resize.js'; const room = () => ({ @@ -27,6 +32,7 @@ const setup = () => { snapshotIdentity: 'snapshot-a', before: { id: 'before' }, wallUnionBefore: { path: 'old' }, + epochBefore: 42, }), true); return { controller, beforeWalls }; }; @@ -128,12 +134,107 @@ test('#264 exact wall profile fails closed on preview and is rechecked on finish test('#264 cancel restores retained masonry only for the same snapshot', () => { const same = setup().controller; assert.deepEqual(same.cancel('snapshot-a'), { - kind: 'cancelled', restoreWallUnion: { path: 'old' }, + kind: 'cancelled', restoreWallUnion: { path: 'old' }, restoreEpoch: 42, }); assert.deepEqual(same.cancel('snapshot-a'), { kind: 'no-op' }); const stale = setup().controller; assert.deepEqual(stale.cancel('snapshot-b'), { - kind: 'cancelled', restoreWallUnion: null, + kind: 'cancelled', restoreWallUnion: null, restoreEpoch: null, }); }); +test('#451 live resize preflight keeps only affected rooms at any plan size', () => { + const rect = (id, x, y, w = 1, h = 1) => ({ + id, poly: [[x, y], [x + w, y], [x + w, y + h], [x, y + h]], + }); + const rooms = Array.from({ length: 100 }, (_, index) => + rect(`room-${index}`, index % 10, Math.floor(index / 10))); + assert.deepEqual( + resizeLiveRoomIds(rooms, ['room-44']), + ['room-44'], + ); + assert.deepEqual(resizeLiveRoomIds([ + rect('changed', 0, 0, 2, 2), rect('nested', 0.5, 0.5, 0.5, 0.5), + rect('endpoint', 2, 2), rect('remote', 10, 10), + ], ['changed']), ['changed']); + assert.deepEqual(resizeLiveJunctionRoomIds([ + rect('changed', 0, 0, 2, 2), rect('nested', 0.5, 0.5, 0.5, 0.5), + rect('endpoint', 2, 2), rect('remote', 10, 10), + ], ['changed']), ['changed', 'nested', 'endpoint']); +}); + +test('#451 live resize candidate keeps nearby physical bodies and excludes remote work', () => { + const rect = (id, x, y) => ({ + id, poly: [[x, y], [x + 1, y], [x + 1, y + 1], [x, y + 1]], + }); + const rooms = Array.from({ length: 100 }, (_, index) => + rect(`room-${index}`, index % 10, Math.floor(index / 10))); + const candidate = resizeLiveCandidateSpace({ + id: 'large', cell_cm: 5, rooms, + walls: [ + { id: 'near', key: '4.5,4.0@0.0000', a: [4, 4], b: [5, 4] }, + { id: 'remote', key: '90.5,90.0@0.0000', a: [90, 90], b: [91, 90] }, + { id: 'unknown-legacy-data', cm: 15 }, + ], + wall_segments: [ + { id: 'near', a: [4, 4], b: [5, 4] }, + { id: 'remote', a: [90, 90], b: [91, 90] }, + ], + open_spans: [ + { id: 'near', a: [4, 4], b: [5, 4] }, + { id: 'remote', a: [90, 90], b: [91, 90] }, + ], + partitions: [ + { id: 'near', a: [4.5, 4.5], b: [5, 5] }, + { id: 'remote', a: [90, 90], b: [91, 91] }, + ], + room_drafts: [ + { id: 'near', points: [[3, 3], [4, 3], [4, 4]] }, + { id: 'remote', points: [[90, 90], [91, 90], [91, 91]] }, + ], + wall_columns: [ + { id: 'near', center: [4, 4.5], cm: 15 }, + { id: 'remote', center: [90, 90], cm: 15 }, + ], + openings: [ + { id: 'near', x: 4.5, y: 4, length: 1 }, + { id: 'remote', x: 90, y: 90, length: 1 }, + ], + }, ['room-44']); + assert.ok(candidate); + assert.equal(candidate.rooms.length, 1); + assert.deepEqual(candidate.walls.map((item) => item.id), ['near', 'unknown-legacy-data']); + assert.deepEqual(candidate.wall_segments.map((item) => item.id), ['near']); + assert.deepEqual(candidate.open_spans.map((item) => item.id), ['near']); + assert.deepEqual(candidate.partitions.map((item) => item.id), ['near']); + assert.deepEqual(candidate.room_drafts.map((item) => item.id), ['near']); + assert.deepEqual(candidate.wall_columns.map((item) => item.id), ['near']); + assert.deepEqual(candidate.openings.map((item) => item.id), ['near']); +}); + +test('#451 live resize physical preflight retains the adjacent room of a degraded wall', () => { + const fixture = JSON.parse(readFileSync( + new URL('./fixtures/278-wall-union-isolation.json', import.meta.url), 'utf8', + )); + const baseSpace = fixture.config.spaces[0]; + const remoteRooms = Array.from({ length: 80 }, (_, index) => { + const x = 10 + index * 2; + return { + id: `remote-${index}`, + poly: [[x, 10], [x + 1, 10], [x + 1, 11], [x, 11]], + }; + }); + const largeSpace = { ...baseSpace, rooms: [...baseSpace.rooms, ...remoteRooms] }; + const full = checkSpacePhysicalGeometry({ spaces: [largeSpace] }, largeSpace.id); + assert.equal(full.ok, false); + assert.equal(full.reason, 'wall-degraded-extra'); + + const liveRoomIds = resizeLiveJunctionRoomIds(largeSpace.rooms, ['r1']); + assert.deepEqual(liveRoomIds, ['r1', 'r2']); + const liveSpace = resizeLiveCandidateSpace(largeSpace, liveRoomIds); + assert.ok(liveSpace); + assert.deepEqual(liveSpace.rooms.map((room) => room.id), ['r1', 'r2']); + const live = checkSpacePhysicalGeometry({ spaces: [liveSpace] }, liveSpace.id); + assert.equal(live.ok, false); + assert.equal(live.reason, 'wall-degraded-extra'); +}); diff --git a/tsconfig.test.json b/tsconfig.test.json index bec8b271..71051135 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -27,7 +27,7 @@ "src/space-order.ts", "src/card-editor-validation.ts", "src/signing.ts", "src/initial-load.ts", "src/space-model-selection.ts", "src/space-dialog.ts", "src/visual-continuity.ts", "src/mode-transition.ts", "src/viewport-transition.ts", "src/room-fit.ts", "src/editor-runtime-loader.ts", "src/editor-secondary.ts", "src/pointer-modality.ts", - "src/render-device-snapshot.ts", + "src/render-device-snapshot.ts", "src/render-invalidation.ts", "src/houseplan-render-lifecycle.ts", "src/live-interaction-runtime.ts", "src/live-viewport.ts", "src/live-hover.ts", "src/live-editor.ts", "src/pointer-move-queue.ts", "src/command-stack.ts", "src/device-position-history.ts", "src/coordinate-canonicalization.ts", "src/serialized-write-queue.ts",