From 6efc315ba62e9b62ad5d95bbd0d2bdecb7d967ba Mon Sep 17 00:00:00 2001 From: Matysh Date: Thu, 27 Aug 2026 21:53:10 +0300 Subject: [PATCH] refactor: make device presentation decisions explicit Issue: #267 User-Visible: no --- .../houseplan/frontend/houseplan-card.js | 144 ++-- dist/houseplan-card.js | 144 ++-- docs/ARCHITECTURE.md | 7 +- docs/DEVICE-PRESENTATION.md | 101 +++ docs/STATUS.md | 2 +- docs/images/screenshots.json | 22 +- scripts/mutation-gate.mjs | 155 ++++- src/device-presentation-policy.ts | 197 ++++++ src/device-presentation.ts | 157 +++-- test/device-presentation-policy.test.mjs | 629 ++++++++++++++++++ .../device-presentation-decisions.mjs | 66 ++ 11 files changed, 1379 insertions(+), 245 deletions(-) create mode 100644 docs/DEVICE-PRESENTATION.md create mode 100644 src/device-presentation-policy.ts create mode 100644 test/device-presentation-policy.test.mjs create mode 100644 test/fixtures/device-presentation-decisions.mjs diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index c01d6c4a..76e7d7e6 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb9222009de6831f5174df777aa8a17d9";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(e,t,s){if(this._$cssResult$=!0,s!==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=s.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&s.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const s=1===e.length?e[0]:t.reduce((t,i,s)=>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[s+1],e[0]);return new n(s,e,i)},a=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new n("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m: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)=>!r(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.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(),s=this.getPropertyDescriptor(e,i,t);void 0!==s&&l(this.prototype,e,s)}}static getPropertyDescriptor(e,t,i){const{get:s,set:n}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:s,set(t){const o=s?.call(this);n?.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=p(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(a(e))}else void 0!==e&&t.push(a(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,s)=>{if(t)i.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of s){const s=document.createElement("style"),n=e.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=t.cssText,i.appendChild(s)}})(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),s=this.constructor._$Eu(e,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(e,t){const i=this.constructor,s=i._$Eh.get(e);if(void 0!==s&&this._$Em!==s){const e=i.getPropertyOptions(s),n="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=s;const o=n.fromAttribute(t,e.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(e,t,i,s=!1,n){if(void 0!==e){const o=this.constructor;if(!1===s&&(n=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(n,t)||i.useDefault&&i.reflect&&n===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:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==n||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===s&&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,s=this[t];!0!==e||this._$AL.has(t)||void 0===s||this.C(t,void 0,i,s)}}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}),(u.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)}$`,D="?"+C,P=`<${D}>`,T=document,I=()=>T.createComment(""),R=e=>null===e||"object"!=typeof e&&"function"!=typeof e,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \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"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(e,t){if(!A(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const J=(e,t)=>{const i=e.length-1,s=[];let n,o=2===t?"":3===t?"":"",a=F;for(let t=0;t"===l[0]?(a=n??F,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,r=l[1],a=void 0===l[3]?O:'"'===l[3]?L:H):a===L||a===H?a=O:a===E||a===N?a=F:(a=O,n=void 0);const d=a===O&&e[t+1].startsWith("/>")?" ":"";o+=a===F?i+P:c>=0?(s.push(r),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?"":"")),s]};class X{constructor({strings:e,_$litType$:t},i){let s;this.parts=[];let n=0,o=0;const a=e.length-1,r=this.parts,[l,c]=J(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(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!==G&&R(this._$AH)?this._$AA.nextSibling.data=e:this.T(T.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,s="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(t);else{const e=new Q(s,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=V.get(e.strings);return void 0===t&&V.set(e.strings,t=new X(e)),t}k(e){A(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,s=0;for(const n of e)s===t.length?t.push(i=new ee(this.O(I()),this.O(I()),this,this.options)):i=t[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(e,t=this,i,s){const n=this.strings;let o=!1;if(void 0===n)e=Z(this,e,t,0),o=!R(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const s=e;let a,r;for(e=n[0],a=0;a{const s=i?.renderBefore??t;let n=s._$litPart$;if(void 0===n){const e=i?.renderBefore??null;s._$litPart$=n=new ee(t.insertBefore(I(),e),e,void 0,i??{})}return n._$AI(e),n},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 pe=2,ue=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const me={},ge=ue(class extends _e{constructor(){super(...arguments),this.ot=me}render(e,t){return t()}update(e,[t,i]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((e,t)=>e===this.ot[t]))return U}else if(this.ot===t)return U;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,i)}}),{I:fe}=ae,ve=e=>e,ye=()=>document.createComment(""),be=(e,t,i)=>{const s=e._$AA.parentNode,n=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=s.insertBefore(ye(),n),o=s.insertBefore(ye(),n);i=new fe(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,a=o!==e;if(a){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==n||a){let e=i._$AA;for(;e!==t;){const t=ve(e).nextSibling;ve(s).insertBefore(e,n),e=t}}}return i},we=(e,t,i=e)=>(e._$AI(t,i),e),ke={},xe=(e,t=ke)=>e._$AH=t,$e=e=>{e._$AR(),e._$AA.remove()},Se=(e,t,i)=>{const s=new Map;for(let n=t;n<=i;n++)s.set(e[n],n);return s},Me=ue(class extends _e{constructor(e){if(super(e),e.type!==pe)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let s;void 0===i?i=t:void 0!==t&&(s=t);const n=[],o=[];let a=0;for(const t of e)n[a]=s?s(t,a):a,o[a]=i(t,a),a++;return{values:o,keys:n}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,s]){const n=(e=>e._$AH)(e),{values:o,keys:a}=this.dt(t,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=we(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=we(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=we(n[d],o[_]),be(e,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=we(n[p],o[u]),be(e,n[d],n[p]),p--,u++;else if(void 0===c&&(c=Se(a,u,_),h=Se(r,d,p)),c.has(r[d]))if(c.has(r[p])){const t=h.get(a[u]),i=void 0!==t?n[t]:null;if(null===i){const t=be(e,n[d]);we(t,o[u]),l[u]=t}else l[u]=we(i,o[u]),be(e,n[d],i),n[t]=null;u++}else $e(n[p]),p--;else $e(n[d]),d++;for(;u<=_;){const t=be(e,l[_+1]);we(t,o[u]),l[u++]=t}for(;d<=p;){const e=n[d++];null!==e&&$e(e)}return this.ut=a,xe(e,l),U}}),Ce=new WeakMap;let De=0;class Pe extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,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-"+ ++De,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?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._useHaDialog)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 s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!e.shiftKey||s!==n&&i.includes(s)?e.shiftKey||s!==o||(e.preventDefault(),n.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Ce.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Ce.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 s=t?Ce.get(t):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),t&&s&&requestAnimationFrame(()=>{const e=Ce.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Ce.delete(t);const s=i?.closest("hp-dialog");i?.isConnected&&s&&e.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){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,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(e)&&t.push(n),n instanceof HTMLSlotElement){for(const e of n.assignedNodes({flatten:!0}))s(e);return}if(n.shadowRoot){for(const e of n.shadowRoot.childNodes)s(e);return}}for(const e of n.childNodes)s(e)}};for(const e of this.childNodes)s(e);const n=this.overlayPortal();return n&&s(n),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})}_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 s={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const e=this._overlays.findIndex(e=>e.token===s.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__="3d7cafaabdfd8cbcf261f9dccec794dd7c424655b825df5e29782f819156ebbf";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(e,t,s){if(this._$cssResult$=!0,s!==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=s.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&s.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const s=1===e.length?e[0]:t.reduce((t,i,s)=>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[s+1],e[0]);return new n(s,e,i)},a=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new n("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m: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)=>!r(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.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(),s=this.getPropertyDescriptor(e,i,t);void 0!==s&&l(this.prototype,e,s)}}static getPropertyDescriptor(e,t,i){const{get:s,set:n}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:s,set(t){const o=s?.call(this);n?.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=p(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(a(e))}else void 0!==e&&t.push(a(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,s)=>{if(t)i.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of s){const s=document.createElement("style"),n=e.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=t.cssText,i.appendChild(s)}})(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),s=this.constructor._$Eu(e,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(e,t){const i=this.constructor,s=i._$Eh.get(e);if(void 0!==s&&this._$Em!==s){const e=i.getPropertyOptions(s),n="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=s;const o=n.fromAttribute(t,e.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(e,t,i,s=!1,n){if(void 0!==e){const o=this.constructor;if(!1===s&&(n=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(n,t)||i.useDefault&&i.reflect&&n===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:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==n||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===s&&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,s=this[t];!0!==e||this._$AL.has(t)||void 0===s||this.C(t,void 0,i,s)}}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}),(u.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)}$`,D="?"+C,P=`<${D}>`,T=document,I=()=>T.createComment(""),R=e=>null===e||"object"!=typeof e&&"function"!=typeof e,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/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"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(e,t){if(!A(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const J=(e,t)=>{const i=e.length-1,s=[];let n,o=2===t?"":3===t?"":"",a=F;for(let t=0;t"===l[0]?(a=n??F,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,r=l[1],a=void 0===l[3]?O:'"'===l[3]?H:L):a===H||a===L?a=O:a===E||a===N?a=F:(a=O,n=void 0);const d=a===O&&e[t+1].startsWith("/>")?" ":"";o+=a===F?i+P:c>=0?(s.push(r),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?"":"")),s]};class X{constructor({strings:e,_$litType$:t},i){let s;this.parts=[];let n=0,o=0;const a=e.length-1,r=this.parts,[l,c]=J(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(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!==G&&R(this._$AH)?this._$AA.nextSibling.data=e:this.T(T.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,s="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(t);else{const e=new Q(s,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=V.get(e.strings);return void 0===t&&V.set(e.strings,t=new X(e)),t}k(e){A(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,s=0;for(const n of e)s===t.length?t.push(i=new ee(this.O(I()),this.O(I()),this,this.options)):i=t[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(e,t=this,i,s){const n=this.strings;let o=!1;if(void 0===n)e=Z(this,e,t,0),o=!R(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const s=e;let a,r;for(e=n[0],a=0;a{const s=i?.renderBefore??t;let n=s._$litPart$;if(void 0===n){const e=i?.renderBefore??null;s._$litPart$=n=new ee(t.insertBefore(I(),e),e,void 0,i??{})}return n._$AI(e),n},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 pe=2,ue=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const me={},ge=ue(class extends _e{constructor(){super(...arguments),this.ot=me}render(e,t){return t()}update(e,[t,i]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((e,t)=>e===this.ot[t]))return U}else if(this.ot===t)return U;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,i)}}),{I:fe}=ae,ve=e=>e,ye=()=>document.createComment(""),be=(e,t,i)=>{const s=e._$AA.parentNode,n=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=s.insertBefore(ye(),n),o=s.insertBefore(ye(),n);i=new fe(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,a=o!==e;if(a){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==n||a){let e=i._$AA;for(;e!==t;){const t=ve(e).nextSibling;ve(s).insertBefore(e,n),e=t}}}return i},we=(e,t,i=e)=>(e._$AI(t,i),e),ke={},xe=(e,t=ke)=>e._$AH=t,$e=e=>{e._$AR(),e._$AA.remove()},Se=(e,t,i)=>{const s=new Map;for(let n=t;n<=i;n++)s.set(e[n],n);return s},Me=ue(class extends _e{constructor(e){if(super(e),e.type!==pe)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let s;void 0===i?i=t:void 0!==t&&(s=t);const n=[],o=[];let a=0;for(const t of e)n[a]=s?s(t,a):a,o[a]=i(t,a),a++;return{values:o,keys:n}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,s]){const n=(e=>e._$AH)(e),{values:o,keys:a}=this.dt(t,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=we(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=we(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=we(n[d],o[_]),be(e,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=we(n[p],o[u]),be(e,n[d],n[p]),p--,u++;else if(void 0===c&&(c=Se(a,u,_),h=Se(r,d,p)),c.has(r[d]))if(c.has(r[p])){const t=h.get(a[u]),i=void 0!==t?n[t]:null;if(null===i){const t=be(e,n[d]);we(t,o[u]),l[u]=t}else l[u]=we(i,o[u]),be(e,n[d],i),n[t]=null;u++}else $e(n[p]),p--;else $e(n[d]),d++;for(;u<=_;){const t=be(e,l[_+1]);we(t,o[u]),l[u++]=t}for(;d<=p;){const e=n[d++];null!==e&&$e(e)}return this.ut=a,xe(e,l),U}}),Ce=new WeakMap;let De=0;class Pe extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,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-"+ ++De,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?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._useHaDialog)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 s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!e.shiftKey||s!==n&&i.includes(s)?e.shiftKey||s!==o||(e.preventDefault(),n.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Ce.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Ce.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 s=t?Ce.get(t):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),t&&s&&requestAnimationFrame(()=>{const e=Ce.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Ce.delete(t);const s=i?.closest("hp-dialog");i?.isConnected&&s&&e.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){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,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(e)&&t.push(n),n instanceof HTMLSlotElement){for(const e of n.assignedNodes({flatten:!0}))s(e);return}if(n.shadowRoot){for(const e of n.shadowRoot.childNodes)s(e);return}}for(const e of n.childNodes)s(e)}};for(const e of this.childNodes)s(e);const n=this.overlayPortal();return n&&s(n),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})}_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 s={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const e=this._overlays.findIndex(e=>e.token===s.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``:G} ${this.title} `;return this._useHaDialog?W`"number"==typeof e&&Number.isFinite(e)))return null;const[i,s,n]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${s}, ${n})`}const Fe=(e,t,i)=>Math.min(i,Math.max(t,Number.isFinite(e)?e:t));function Ee(e){return Number.isFinite(e)?(e%360+360)%360:0}function Ne(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^#/,""),i=/^[0-9a-fA-F]{3}$/.test(t)?t.split("").map(e=>`${e}${e}`).join(""):t;return/^[0-9a-fA-F]{6}$/.test(i)?`#${i.toLowerCase()}`:null}function Oe(e){const t=Ne(e);return t?{r:Number.parseInt(t.slice(1,3),16),g:Number.parseInt(t.slice(3,5),16),b:Number.parseInt(t.slice(5,7),16)}:null}function He(e){const t=Fe(e.r,0,255)/255,i=Fe(e.g,0,255)/255,s=Fe(e.b,0,255)/255,n=Math.max(t,i,s),o=n-Math.min(t,i,s);let a=0;return o>0&&(a=n===t?(i-s)/o%6*60:n===i?60*((s-t)/o+2):60*((t-i)/o+4)),{h:Ee(a),s:0===n?0:o/n*100,v:100*n}}function Le(e){return function(e){const t=e=>Math.round(Fe(e,0,255)).toString(16).padStart(2,"0");return`#${t(e.r)}${t(e.g)}${t(e.b)}`}(function(e){const t=Ee(e.h),i=Fe(e.s,0,100)/100,s=Fe(e.v,0,100)/100,n=s*i,o=n*(1-Math.abs(t/60%2-1)),a=s-n;let r;return r=t<60?[n,o,0]:t<120?[o,n,0]:t<180?[0,n,o]:t<240?[0,o,n]:t<300?[o,0,n]:[n,0,o],{r:Math.round(255*(r[0]+a)),g:Math.round(255*(r[1]+a)),b:Math.round(255*(r[2]+a))}}(e))}function Be(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 qe(e,t,i,s=7,n=8){const o=i.left+n,a=i.top+n,r=Math.max(o,i.left+i.width-n),l=Math.max(a,i.top+i.height-n),c=Math.max(0,r-o),h=Math.max(0,l-a),d=Math.min(Math.max(0,t.width),c),p=Math.min(Math.max(0,t.height),h);let u=e.left;u+d>r&&(u=e.right-d),u=Math.min(Math.max(o,u),Math.max(o,r-d));const _=e.bottom+s,m=e.top-s-p;let g="bottom",f=_;return _+p>l&&m>=a?(g="top",f=Math.min(Math.max(a,m),Math.max(a,l-p))):f=Math.min(Math.max(a,_),Math.max(a,l-p)),{left:Math.round(u),top:Math.round(f),side:g,maxWidth:Math.round(c),maxHeight:Math.round(h)}}class We{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(e,t){const i=this.ensureFallback();return i?(le(W`${e}`,i),i):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&le(G,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}}const je={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ue extends he{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=je,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=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closePicker(!1,"outside")},this._keyDown=e=>{this._open&&"Escape"===e.key&&(e.preventDefault(),e.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new We(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const e=this._window();e&&(this._pickerRaf=e.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 e=this.ownerDocument.defaultView;e?.addEventListener("resize",this._queuePickerPosition),e?.addEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.addEventListener("resize",this._queuePickerPosition),e?.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 e=this.ownerDocument.defaultView;e?.removeEventListener("resize",this._queuePickerPosition),e?.removeEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),e?.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(e){if(e.has("color")){const e=Ne(this.color);e&&e!==this._lastValidColor&&this._syncFromColor(e,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(e.has("color")||e.has("opacity")||e.has("showOpacity")||e.has("label")||e.has("opacityLabel")||e.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:e=>this._closePicker("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closePicker())))}_closePicker(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".picker");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const e=Ue.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),e)}_positionPicker(){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 s=Be(e);if(i.style.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._supportsPopover()&&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.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden"}const n=t.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=qe(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}_labels(){const e=this.pickerLabels||je;return{title:e.title||je.title,hue:e.hue||je.hue,saturation:e.saturation||je.saturation,value:e.value||je.value,hex:e.hex||je.hex,invalidHex:e.invalidHex||je.invalidHex}}_syncFromColor(e,t){const i=Ne(Ie(e,"#607d8b"))||"#607d8b",s=Oe(i);if(!s)return;const n=He(s);(!t||n.s>1e-4)&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._hexDraft=i,this._hexInvalid=!1,this._lastValidColor=i}_cancelQueuedEmit(e=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,e&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const e=this._window();e?this._emitRaf=e.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Le({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(e){this._hue=Math.min(359,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setSaturation(e){this._saturation=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setValue(e){this._value=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setOpacity(e){this._emit(this._lastValidColor,e)}_shiftRangeKey(e,t,i,s,n){if(!e.shiftKey)return;const o="ArrowRight"===e.key||"ArrowUp"===e.key?10:"ArrowLeft"===e.key||"ArrowDown"===e.key?-10:0;o&&(e.preventDefault(),e.stopPropagation(),n(Math.min(s,Math.max(i,t+o))))}_setSvFromPointer(e,t){const i=e.currentTarget.getBoundingClientRect();i.width&&i.height&&(this._saturation=Math.min(100,Math.max(0,(e.clientX-i.left)/i.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(e.clientY-i.top)/i.height))),t?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(e){if(e.preventDefault(),e.stopPropagation(),null===this._activePointerId){this._activePointerId=e.pointerId;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}this._setSvFromPointer(e,!0)}}_svPointerMove(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!1))}_svPointerUp(e){if(e.pointerId===this._activePointerId){e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!0),this._activePointerId=null;try{e.currentTarget.releasePointerCapture(e.pointerId)}catch{}}}_svPointerCancel(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(e){e.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(e){const t=e.target.value;this._hexDraft=t,this._hexInvalid=!1;const i=Ne(t),s=i?Oe(i):null;if(!i||!s)return;const n=He(s);n.s>1e-4&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._emit(i,this.opacity,!0)}_commitHex(){const e=Ne(this._hexDraft);if(!e)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=e,this._hexInvalid=!1,e!==this._lastValidColor&&this._emit(e,this.opacity)}_hexKeyDown(e){"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),this._commitHex())}_emit(e,t,i=!1){const s=Ne(e)||this._lastValidColor,n=Number.isFinite(t)?t:Number(this.opacity)||0,o=Math.min(1,Math.max(0,n));this.color=s,this.opacity=o,this._lastValidColor=s,i||(this._hexDraft=s),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:s,opacity:o},bubbles:!0,composed:!0}))}_pickerTemplate(e){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),i=Le({h:this._hue,s:this._saturation,v:this._value}),s=Le({h:this._hue,s:100,v:100}),n=this._labels(),o=Math.round(this._hue),a=Math.round(this._saturation),r=Math.round(this._value);return W` + `,customElements.get("hp-dialog")||customElements.define("hp-dialog",Pe);const Te=/^#[0-9a-fA-F]{6}$/;function Ie(e,t){return"string"==typeof e&&Te.test(e)?e:t}const Re="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",Ae=new RegExp(`^rgb\\(${Re}, ${Re}, ${Re}\\)$`);function ze(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,s,n]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${s}, ${n})`}const Fe=(e,t,i)=>Math.min(i,Math.max(t,Number.isFinite(e)?e:t));function Ee(e){return Number.isFinite(e)?(e%360+360)%360:0}function Ne(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^#/,""),i=/^[0-9a-fA-F]{3}$/.test(t)?t.split("").map(e=>`${e}${e}`).join(""):t;return/^[0-9a-fA-F]{6}$/.test(i)?`#${i.toLowerCase()}`:null}function Oe(e){const t=Ne(e);return t?{r:Number.parseInt(t.slice(1,3),16),g:Number.parseInt(t.slice(3,5),16),b:Number.parseInt(t.slice(5,7),16)}:null}function Le(e){const t=Fe(e.r,0,255)/255,i=Fe(e.g,0,255)/255,s=Fe(e.b,0,255)/255,n=Math.max(t,i,s),o=n-Math.min(t,i,s);let a=0;return o>0&&(a=n===t?(i-s)/o%6*60:n===i?60*((s-t)/o+2):60*((t-i)/o+4)),{h:Ee(a),s:0===n?0:o/n*100,v:100*n}}function He(e){return function(e){const t=e=>Math.round(Fe(e,0,255)).toString(16).padStart(2,"0");return`#${t(e.r)}${t(e.g)}${t(e.b)}`}(function(e){const t=Ee(e.h),i=Fe(e.s,0,100)/100,s=Fe(e.v,0,100)/100,n=s*i,o=n*(1-Math.abs(t/60%2-1)),a=s-n;let r;return r=t<60?[n,o,0]:t<120?[o,n,0]:t<180?[0,n,o]:t<240?[0,o,n]:t<300?[o,0,n]:[n,0,o],{r:Math.round(255*(r[0]+a)),g:Math.round(255*(r[1]+a)),b:Math.round(255*(r[2]+a))}}(e))}function Be(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 qe(e,t,i,s=7,n=8){const o=i.left+n,a=i.top+n,r=Math.max(o,i.left+i.width-n),l=Math.max(a,i.top+i.height-n),c=Math.max(0,r-o),h=Math.max(0,l-a),d=Math.min(Math.max(0,t.width),c),p=Math.min(Math.max(0,t.height),h);let u=e.left;u+d>r&&(u=e.right-d),u=Math.min(Math.max(o,u),Math.max(o,r-d));const _=e.bottom+s,m=e.top-s-p;let g="bottom",f=_;return _+p>l&&m>=a?(g="top",f=Math.min(Math.max(a,m),Math.max(a,l-p))):f=Math.min(Math.max(a,_),Math.max(a,l-p)),{left:Math.round(u),top:Math.round(f),side:g,maxWidth:Math.round(c),maxHeight:Math.round(h)}}class We{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(e,t){const i=this.ensureFallback();return i?(le(W`${e}`,i),i):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&le(G,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}}const je={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ue extends he{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=je,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=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closePicker(!1,"outside")},this._keyDown=e=>{this._open&&"Escape"===e.key&&(e.preventDefault(),e.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new We(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const e=this._window();e&&(this._pickerRaf=e.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 e=this.ownerDocument.defaultView;e?.addEventListener("resize",this._queuePickerPosition),e?.addEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.addEventListener("resize",this._queuePickerPosition),e?.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 e=this.ownerDocument.defaultView;e?.removeEventListener("resize",this._queuePickerPosition),e?.removeEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),e?.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(e){if(e.has("color")){const e=Ne(this.color);e&&e!==this._lastValidColor&&this._syncFromColor(e,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(e.has("color")||e.has("opacity")||e.has("showOpacity")||e.has("label")||e.has("opacityLabel")||e.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:e=>this._closePicker("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closePicker())))}_closePicker(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".picker");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const e=Ue.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),e)}_positionPicker(){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 s=Be(e);if(i.style.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._supportsPopover()&&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.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden"}const n=t.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=qe(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}_labels(){const e=this.pickerLabels||je;return{title:e.title||je.title,hue:e.hue||je.hue,saturation:e.saturation||je.saturation,value:e.value||je.value,hex:e.hex||je.hex,invalidHex:e.invalidHex||je.invalidHex}}_syncFromColor(e,t){const i=Ne(Ie(e,"#607d8b"))||"#607d8b",s=Oe(i);if(!s)return;const n=Le(s);(!t||n.s>1e-4)&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._hexDraft=i,this._hexInvalid=!1,this._lastValidColor=i}_cancelQueuedEmit(e=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,e&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const e=this._window();e?this._emitRaf=e.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(He({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(e){this._hue=Math.min(359,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setSaturation(e){this._saturation=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setValue(e){this._value=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setOpacity(e){this._emit(this._lastValidColor,e)}_shiftRangeKey(e,t,i,s,n){if(!e.shiftKey)return;const o="ArrowRight"===e.key||"ArrowUp"===e.key?10:"ArrowLeft"===e.key||"ArrowDown"===e.key?-10:0;o&&(e.preventDefault(),e.stopPropagation(),n(Math.min(s,Math.max(i,t+o))))}_setSvFromPointer(e,t){const i=e.currentTarget.getBoundingClientRect();i.width&&i.height&&(this._saturation=Math.min(100,Math.max(0,(e.clientX-i.left)/i.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(e.clientY-i.top)/i.height))),t?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(e){if(e.preventDefault(),e.stopPropagation(),null===this._activePointerId){this._activePointerId=e.pointerId;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}this._setSvFromPointer(e,!0)}}_svPointerMove(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!1))}_svPointerUp(e){if(e.pointerId===this._activePointerId){e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!0),this._activePointerId=null;try{e.currentTarget.releasePointerCapture(e.pointerId)}catch{}}}_svPointerCancel(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(e){e.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(e){const t=e.target.value;this._hexDraft=t,this._hexInvalid=!1;const i=Ne(t),s=i?Oe(i):null;if(!i||!s)return;const n=Le(s);n.s>1e-4&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._emit(i,this.opacity,!0)}_commitHex(){const e=Ne(this._hexDraft);if(!e)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=e,this._hexInvalid=!1,e!==this._lastValidColor&&this._emit(e,this.opacity)}_hexKeyDown(e){"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),this._commitHex())}_emit(e,t,i=!1){const s=Ne(e)||this._lastValidColor,n=Number.isFinite(t)?t:Number(this.opacity)||0,o=Math.min(1,Math.max(0,n));this.color=s,this.opacity=o,this._lastValidColor=s,i||(this._hexDraft=s),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:s,opacity:o},bubbles:!0,composed:!0}))}_pickerTemplate(e){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),i=He({h:this._hue,s:this._saturation,v:this._value}),s=He({h:this._hue,s:100,v:100}),n=this._labels(),o=Math.round(this._hue),a=Math.round(this._saturation),r=Math.round(this._value);return W` `}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}render(){if(!this._config)return G;const e=this._snap?.config;if(!e)return this._errorCard(la(this._lang,"space_card.loading"));const t=this._config.space,i=this._renderDeviceSnapshot,s=C_({hass:i?.hass||this.hass,cfg:e,layout:this._snap?.layout||{},spaceId:t,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:e=>this._signer.display(this.hass,e),assetLoaded:this._onAssetLoaded,registry:i?{revision:i.sourceSequence,authoritative:!1,access:"limited",entities:{...i.hass.entities},devices:{...i.hass.devices},lastSuccess:i.capturedAt}:An(this.hass),devices:[...i?.devices||this._devices],presentations:i?.presentations,activityRuntime:this._activityRuntime,reducedMotion:this._reducedMotion,virtualLights:this._snap?.virtualLights,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(la(this._lang,"space_card.not_found",{id:t}));const n=Th(e).find(e=>e.id===t),o=void 0!==this._config.title?this._config.title:n?.title||"",a=!1!==this._config.show_button,r=this._config.button_label||la(this._lang,"space_card.button"),l=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return W` ${r} `:G} - `}}U_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},U_.styles=[Ke,o` + `}}G_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},G_.styles=[Ke,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -4757,7 +4757,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",U_),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 G_=o` + `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",G_),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 V_=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); @@ -4928,7 +4928,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 .editor-secondary { transition: none; } .editor-group-launcher .group-chevron { transition: none; } } -`,V_=(e,t)=>(e.composedPath?.()||[e.target]).some(e=>(e=>!!e&&"function"==typeof e.matches)(e)&&t(e));class K_{constructor(e){this.host=e,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=e=>{this.handleOutsideDismiss(e)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(e){return this._openGroupId&&e.find(e=>e.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 e="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,t=!!this._openGroupId||!!e;this._openGroupId=null,e?.(),e&&(this._currentModel=null),this._clearDismissClick(),t&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(e,t){this._openGroupId!==t?e.some(e=>e.id===t)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=t,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(e){const t=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),e&&t&&this.host.updateComplete().then(()=>{const e=this.host.root(),i=[...e.querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t&&null!==e.offsetParent);(i||e.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(e,t,i){const s=this._openGroupId===e.id,n=e.items.find(t=>t.id===e.activeItemId&&!t.disabled);return W``:G} - `}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],s=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]:"")+","+(s?s[0]+"/"+s[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 Th(e).map((t,i)=>{const s=e.spaces[i]?.plan_url;return t.bg&&s?{...t,bg:{...t.bg,href:s}}:t})}_spaceModel(){return this._hasFixedFloor?Td(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Td(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!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._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),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._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=Nn(t,i),n=On(t,i),{areaToSpace:o,markers:a,settings:r,excluded:l,iconRules:c}=e,h=!1!==r.group_lights,d=wo(a),p=yo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=qn(s),m=Wn(a,n),g=new Set(a.map(e=>e.binding)),f=[];for(const e of Object.values(s.devices)){const n=e.area_id;if(!n||!o[n])continue;if("service"===e.entry_type)continue;if(g.has("device:"+e.id))continue;if("active"!==Bn(t,"device:"+e.id,i).kind)continue;const a=jn(s,e.id,_[e.id]||[],m);if(a.partial&&!a.entityIds.length)continue;const r=a.entityIds,d=Un(s,e,r);let p=l.has(d)||"Group"===e.model||/scene/i.test(e.model||"")||/bridge/i.test((e.model||"")+(e.name||""))||"myheat"===d&&!!e.via_device_id;if(!p&&h&&u.has(n)){const t=(e.name_by_user||e.name||"").trim();"mdi:lightbulb"===bo(s,t,e.model,r,c)&&(p=!0)}p&&f.push("device:"+e.id)}return f}({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 s of t){const t="h"+s.slice(s.indexOf(":")+1);e.markers.push({id:t,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...e.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(e=>!i.includes(e))),e.settings=s,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=Xe(e)),this._rulesCompiled}get _fillColors(){return ps(this._settings)}get _excluded(){const e=this._settings.exclude_integrations;return e?new Set(e):Ye}willUpdate(e){e.has("_serverCfg")&&this._cfgEpoch++,this._syncEmptySpaceState(),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._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,s=Math.min(Math.max(t.getBoundingClientRect().top,0),120),n=Math.round(i+s);n>=0&&n!==this._hdrH&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:n,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,s=t.level??1e9;return i!==s?i-s:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptStructuralResponses(e,t,i){const s=e?.config,n=s&&Array.isArray(s.spaces)?s:null,o=Md(n),a=o!==(this._cfgContentFingerprint||Md(this._serverCfg));if(a&&(this._geometryHistory.clear(),this._pendingPhysicalWrites.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=n,this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=gn(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);vn(t)!==vn(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let r=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},s=Md(e);r=s!==(this._layoutContentFingerprint||Md(this._layout)),r&&(this._layout=e,this._layoutContentFingerprint=s),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._haIntegrationVersion="string"==typeof e?.integration_version?e.integration_version:this._haIntegrationVersion,this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),a&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),r&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:a,layoutChanged:r}}_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,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),n=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Md(n)!==(this._cfgContentFingerprint||Md(this._serverCfg))||Md(s?.layout??{})!==(this._layoutContentFingerprint||Md(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(n)))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&&!s?.can_optimize_undo),this._haIntegrationVersion="string"==typeof i?.integration_version?i.integration_version:this._haIntegrationVersion,this._adoptStructuralResponses(i,s),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(tv)||"{}")||{}}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=[],s=(s,n,o,a)=>{s()||i.push(async()=>{const i=await e.subscribeEvents(a,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!s()?n(i):i?.()})};if(s(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),s(()=>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{}}),s(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),s(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=wn(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const n=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=n,n.finally(()=>{this._liveSyncAttempt===n&&(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(Md(t)!==(this._cfgContentFingerprint||Md(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._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._planHassMemo=null,this._haRegistryRelease=Rn(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return An(this.hass)}get _planHass(){const e=this._haRegistry,t=Fn(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const s=Nn(this.hass,e),n=On(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:s,full:n},s}_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+N_)>e?1:0)).join("|"),i=vn(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 s=this._planHass,n=new Map,o=new Map,a=new Set(["sun.sun"]);this._vacFit?.source&&a.add(this._vacFit.source);const r=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void a.add(e);const i=e.slice(0,t),s=e.slice(t+1);"device"===i?r.add(s):"entity"===i&&a.add(s)};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 gi(t))a.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=Yi(e[1]);t?.entity&&a.add(t.entity)}t.entity&&a.add(t.entity),o.set(`decor:${e.id}:${t.id}`,es(t.text,t,s,e=>!!s.entities?.[e]&&!!s.states?.[e]&&!ko(s,e,wo(this._markers))))}const h=ho(s,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])n.set(h_(e.id,t),a_(s,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,s),i=t?Wd(s?.states?.[t]?.attributes):null,n=this._vacRt.get(e.id);o.set(`vacuum:${e.id}`,{source:t,telemetry:i,mapId:i?this._vacMapId(e,i,s):null,runtime:n?{trail:n.trail,lastTs:n.lastTs,moving:n.moving,jump:n.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=c_({sourceSequence:this._hassSequence,hass:s,devices:this._devices,presentations:n,positions:(p=this._model.length>0,u=this._devices,_=e=>this._livePos(e),p?new Map(u.map(e=>[e.id,_(e)])):new Map),facts:o,entityIds:a,deviceIds:r,areaIds:l});var p,u,_;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 _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return Bn(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=An(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&&_v("/config/devices/device/"+encodeURIComponent(e.device_id))}}else _v("/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)}_resign(){this._signer.resign(this.hass,Rs(this._serverCfg))}_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,s]of e)i[t]=s;const s=Md(i);s!==(this._layoutContentFingerprint||Md(this._layout))&&(this._layout=i,this._layoutContentFingerprint=s),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()}}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=Fn(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+ra(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const s=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=So({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 n=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+n.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const s of n)i.set(s,Bn(e,s,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=Ln(),i=Date.now();for(const[s,n]of e)"active"===n.kind?t[s]={kind:"active",ts:i}:"ha_disabled"===n.kind&&(t[s]={kind:"ha_disabled",reason:n.reason,ts:i});const s=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));Mn=s;try{localStorage.setItem(Sn,JSON.stringify(s))}catch{}}(i),this._haBindingCacheKey=o}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),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 r=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==s.get(e.id));r&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._drag&&"ha_disabled"===this._devices.find(e=>e.id===this._drag.id)?.bindingStatus?.kind&&(this._drag=null),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(uv(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_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:s,known:n}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),s=e.filter(e=>!i.has(e));return{fresh:s,known:s.length?[...t,...s]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const e=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:n,new_device_ids:e}},this._saveConfig()}}get _newIds(){const e=this._settings.new_device_ids;return new Set(Array.isArray(e)?e:[])}_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 s=_o(this._renderPlanHass,i.entities);null!=s&&t.push(s)}return Ei(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),s=Math.min(...t),n=Math.min(...i);return{x:s,y:n,w:Math.max(...t)-s,h:Math.max(...i)-n}}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 s=t/100*Xh(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const n=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!n.length)continue;const o=this._roomBounds(t),a=.1*Math.min(o.w,o.h),r=o.w-2*a,l=o.h-2*a,c=Math.max(1,Math.round(Math.sqrt(n.length*r/Math.max(l,1)))),h=Math.ceil(n.length/c),d=r/c,p=l/Math.max(h,1),u=n.map((e,t)=>({x:o.x+a+d*(t%c+.5),y:o.y+a+p*(Math.floor(t/c)+.5)}));Oi(u,o,s,.5*a),n.forEach((t,i)=>e[t.id]=Nh(u[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._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*lv,y:i.y*lv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Nh(Jh(t)):{x:500,y:500}}_savePos(e,t,i){if(this._spaceModelById(e.space)){if(this._norm){const s=this._gridPitch,n=Math.round(t/s)*s,o=Math.round(i/s)*s,a=this._layout[e.id]?.k;this._layout={...this._layout,[e.id]:{s:e.space,x:Hh(n/lv),y:Hh(o/lv),...a?{k:a}:{}}}}else this._layout={...this._layout,[e.id]:{x:Math.round(t),y:Math.round(i)}};this._dirtyPos.add(e.id),this._persistLayout()}}_visualSamples(e){return t_(this._renderPlanHass,e,this._renderDevices,void 0,this._fullRegistryHass).samples}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const s=i?null:this._renderDeviceSnapshot?.presentations.get(h_(e.id,t));return s||a_(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,o_({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=go(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:mo(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 Bs(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_liveHum(e){return this._config?.show_temperature&&e.primary&&fo(this.hass,e.primary)?vo(this.hass,e.entities):null}_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,uv(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}_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(e.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const s=vp(i.tapAction,i.primary?.split(".")[0]),n=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===s){const e=this._toggleIntent(i);if(!e)return;if(!$p(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=$p(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=wn(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:s}=t;this._startDevicePressFeedback(i.id),this.hass.callService(s.domain,s.service,s.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},s=((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 n=this._toggleConfirmationLines(e);if(!n.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:s}),lines:n,initialIntent:e,deviceId:i.id,exec:()=>{const s=this._devices.find(e=>e.id===i.id),n=s?this._toggleIntent(s):null;n&&!$p(n)&&this._showUnavailableToggleTargets(n)||(n&&Mp(e,n)?t(n):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===s)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===s){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),s=this.hass.states[e];if(!t||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||e;return void n(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"===s&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||$p(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Sp.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Sp.has(s.reason))continue;const e=String(s.name||s.entityId||s.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 s=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),n=Number.isFinite(s)?Math.max(.95,Math.min(1,s)):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(n)},{scale:"0.95",offset:.5},{scale:"1"}],a=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,a);const r=()=>{this._devicePressAnimations.get(e)===a&&this._devicePressAnimations.delete(e)};a.addEventListener("finish",r,{once:!0}),a.addEventListener("cancel",r,{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 la(ra(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){const t=`${e}.aria`,i=ra(this.hass,this._config?.language);return ca(i,e)&&ca(i,t)?W``:G}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 s=this._pos(i);t.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,s=Math.cos(i)*e.rlen/2,n=Math.sin(i)*e.rlen/2,o=qh([[e.rx-s,e.ry-n],[e.rx+s,e.ry+n]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){const s=qh("line"===e.kind?[[e.x1*lv,e.y1*i],[e.x2*lv,e.y2*i]]:"text"===e.kind?[[e.x*lv,e.y*i]]:jr({x:e.x*lv,y:e.y*i,w:e.w*lv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=qh(i);e&&t.push(e)}}return jh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:lv,h:lv};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 s=Kh(this._contentItems(e));let n=s.all||Yh(e),o=this._showFar?n:s.core||Yh(e);return t&&t.id===e.id&&i&&t.grow&&(o=hv(t.rect,o),n=hv(t.all,n)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,outliers:s.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),s=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})),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=`${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=uf(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=uf(t,-1)}else t=uf(t,e?1:0);else for(const i of e)t=uf(t,i.codePointAt(0)||0);else t=uf(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:s,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:ef,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?Ec(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,lv,o):null;if(r&&("failed-core"===r.status||"not-applicable"===r.status))throw new Error("wall boolean geometry failed");const l=r?.paperGeom??zc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,lv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||Bc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,lv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?zp(i.partitionHost,s):t.length||"gate"===e.type?Uc(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:s}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(_f([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 s;if("passage"===e.type)s=[];else if("gate"===e.type){const n=10*e.face.side;s=[gf(e,0,[-i,0],[i,0],n,0,.88*t),gf(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[gf(e,0,[-i,0],[i,0],-90,.27*t,.78*t),gf(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[gf(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(s.map(e=>Object.freeze(e)))}}({...e,face:o},n)});return{walls:r?.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,s=e.build(),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=function(e){let t=1/0,i=1/0,s=-1/0,n=-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),s=Math.max(s,e.hinge[0]+o),n=Math.max(n,e.hinge[1]+o)}return _f([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=af({rect:a?hv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=ef,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const s=[],n=[],o=[];let a=0;for(let r=0;r<(e||[]).length;r++){const l=e[r];for(let e=0;e<(l||[]).length;e++){const c=cf(l[e],e>0);if(!(c.length<3||Math.abs(lf(c))<1e-9)){s.push(hf(c,t,i)),a+=c.length;for(let s=0;se.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:s.join(" "),sides:n,contactPath:o.join(" "),edgeCount:a}}(s.walls,ef,n),c={geometry:l,floor:pf(s.floor,o),openings:s.openings,frame:r};return Jf(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,s=this._isoFallback,"iso"===t&&i&&!s.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,s}_scenePoint(e){return"iso"===this._renderProjection?sf(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=nf([e.x,e.y]),i=nf([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=af({rect:this._frameOf().rect,wallHeight:wh(64,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._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
+
`}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],s=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]:"")+","+(s?s[0]+"/"+s[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 Th(e).map((t,i)=>{const s=e.spaces[i]?.plan_url;return t.bg&&s?{...t,bg:{...t.bg,href:s}}:t})}_spaceModel(){return this._hasFixedFloor?Td(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Td(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!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._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),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._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=Nn(t,i),n=On(t,i),{areaToSpace:o,markers:a,settings:r,excluded:l,iconRules:c}=e,h=!1!==r.group_lights,d=wo(a),p=yo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=qn(s),m=Wn(a,n),g=new Set(a.map(e=>e.binding)),f=[];for(const e of Object.values(s.devices)){const n=e.area_id;if(!n||!o[n])continue;if("service"===e.entry_type)continue;if(g.has("device:"+e.id))continue;if("active"!==Bn(t,"device:"+e.id,i).kind)continue;const a=jn(s,e.id,_[e.id]||[],m);if(a.partial&&!a.entityIds.length)continue;const r=a.entityIds,d=Un(s,e,r);let p=l.has(d)||"Group"===e.model||/scene/i.test(e.model||"")||/bridge/i.test((e.model||"")+(e.name||""))||"myheat"===d&&!!e.via_device_id;if(!p&&h&&u.has(n)){const t=(e.name_by_user||e.name||"").trim();"mdi:lightbulb"===bo(s,t,e.model,r,c)&&(p=!0)}p&&f.push("device:"+e.id)}return f}({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 s of t){const t="h"+s.slice(s.indexOf(":")+1);e.markers.push({id:t,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...e.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(e=>!i.includes(e))),e.settings=s,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=Xe(e)),this._rulesCompiled}get _fillColors(){return ps(this._settings)}get _excluded(){const e=this._settings.exclude_integrations;return e?new Set(e):Ye}willUpdate(e){e.has("_serverCfg")&&this._cfgEpoch++,this._syncEmptySpaceState(),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._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,s=Math.min(Math.max(t.getBoundingClientRect().top,0),120),n=Math.round(i+s);n>=0&&n!==this._hdrH&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:n,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,s=t.level??1e9;return i!==s?i-s:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptStructuralResponses(e,t,i){const s=e?.config,n=s&&Array.isArray(s.spaces)?s:null,o=Md(n),a=o!==(this._cfgContentFingerprint||Md(this._serverCfg));if(a&&(this._geometryHistory.clear(),this._pendingPhysicalWrites.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=n,this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=gn(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);vn(t)!==vn(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let r=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},s=Md(e);r=s!==(this._layoutContentFingerprint||Md(this._layout)),r&&(this._layout=e,this._layoutContentFingerprint=s),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._haIntegrationVersion="string"==typeof e?.integration_version?e.integration_version:this._haIntegrationVersion,this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),a&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),r&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:a,layoutChanged:r}}_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,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),n=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Md(n)!==(this._cfgContentFingerprint||Md(this._serverCfg))||Md(s?.layout??{})!==(this._layoutContentFingerprint||Md(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(n)))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&&!s?.can_optimize_undo),this._haIntegrationVersion="string"==typeof i?.integration_version?i.integration_version:this._haIntegrationVersion,this._adoptStructuralResponses(i,s),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(iv)||"{}")||{}}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=[],s=(s,n,o,a)=>{s()||i.push(async()=>{const i=await e.subscribeEvents(a,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!s()?n(i):i?.()})};if(s(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),s(()=>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{}}),s(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),s(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=wn(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const n=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=n,n.finally(()=>{this._liveSyncAttempt===n&&(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(Md(t)!==(this._cfgContentFingerprint||Md(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._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._planHassMemo=null,this._haRegistryRelease=Rn(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return An(this.hass)}get _planHass(){const e=this._haRegistry,t=Fn(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const s=Nn(this.hass,e),n=On(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:s,full:n},s}_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+O_)>e?1:0)).join("|"),i=vn(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 s=this._planHass,n=new Map,o=new Map,a=new Set(["sun.sun"]);this._vacFit?.source&&a.add(this._vacFit.source);const r=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void a.add(e);const i=e.slice(0,t),s=e.slice(t+1);"device"===i?r.add(s):"entity"===i&&a.add(s)};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 gi(t))a.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=Yi(e[1]);t?.entity&&a.add(t.entity)}t.entity&&a.add(t.entity),o.set(`decor:${e.id}:${t.id}`,es(t.text,t,s,e=>!!s.entities?.[e]&&!!s.states?.[e]&&!ko(s,e,wo(this._markers))))}const h=ho(s,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])n.set(d_(e.id,t),r_(s,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,s),i=t?Wd(s?.states?.[t]?.attributes):null,n=this._vacRt.get(e.id);o.set(`vacuum:${e.id}`,{source:t,telemetry:i,mapId:i?this._vacMapId(e,i,s):null,runtime:n?{trail:n.trail,lastTs:n.lastTs,moving:n.moving,jump:n.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=h_({sourceSequence:this._hassSequence,hass:s,devices:this._devices,presentations:n,positions:(p=this._model.length>0,u=this._devices,_=e=>this._livePos(e),p?new Map(u.map(e=>[e.id,_(e)])):new Map),facts:o,entityIds:a,deviceIds:r,areaIds:l});var p,u,_;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 _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return Bn(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=An(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&&mv("/config/devices/device/"+encodeURIComponent(e.device_id))}}else mv("/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)}_resign(){this._signer.resign(this.hass,Rs(this._serverCfg))}_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,s]of e)i[t]=s;const s=Md(i);s!==(this._layoutContentFingerprint||Md(this._layout))&&(this._layout=i,this._layoutContentFingerprint=s),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()}}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=Fn(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+ra(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const s=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=So({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 n=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+n.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const s of n)i.set(s,Bn(e,s,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=Hn(),i=Date.now();for(const[s,n]of e)"active"===n.kind?t[s]={kind:"active",ts:i}:"ha_disabled"===n.kind&&(t[s]={kind:"ha_disabled",reason:n.reason,ts:i});const s=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));Mn=s;try{localStorage.setItem(Sn,JSON.stringify(s))}catch{}}(i),this._haBindingCacheKey=o}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),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 r=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==s.get(e.id));r&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._drag&&"ha_disabled"===this._devices.find(e=>e.id===this._drag.id)?.bindingStatus?.kind&&(this._drag=null),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(_v(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_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:s,known:n}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),s=e.filter(e=>!i.has(e));return{fresh:s,known:s.length?[...t,...s]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const e=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:n,new_device_ids:e}},this._saveConfig()}}get _newIds(){const e=this._settings.new_device_ids;return new Set(Array.isArray(e)?e:[])}_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 s=_o(this._renderPlanHass,i.entities);null!=s&&t.push(s)}return Ei(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),s=Math.min(...t),n=Math.min(...i);return{x:s,y:n,w:Math.max(...t)-s,h:Math.max(...i)-n}}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 s=t/100*Xh(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const n=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!n.length)continue;const o=this._roomBounds(t),a=.1*Math.min(o.w,o.h),r=o.w-2*a,l=o.h-2*a,c=Math.max(1,Math.round(Math.sqrt(n.length*r/Math.max(l,1)))),h=Math.ceil(n.length/c),d=r/c,p=l/Math.max(h,1),u=n.map((e,t)=>({x:o.x+a+d*(t%c+.5),y:o.y+a+p*(Math.floor(t/c)+.5)}));Oi(u,o,s,.5*a),n.forEach((t,i)=>e[t.id]=Nh(u[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._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*cv,y:i.y*cv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Nh(Jh(t)):{x:500,y:500}}_savePos(e,t,i){if(this._spaceModelById(e.space)){if(this._norm){const s=this._gridPitch,n=Math.round(t/s)*s,o=Math.round(i/s)*s,a=this._layout[e.id]?.k;this._layout={...this._layout,[e.id]:{s:e.space,x:Lh(n/cv),y:Lh(o/cv),...a?{k:a}:{}}}}else this._layout={...this._layout,[e.id]:{x:Math.round(t),y:Math.round(i)}};this._dirtyPos.add(e.id),this._persistLayout()}}_visualSamples(e){return i_(this._renderPlanHass,e,this._renderDevices,void 0,this._fullRegistryHass).samples}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const s=i?null:this._renderDeviceSnapshot?.presentations.get(d_(e.id,t));return s||r_(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,a_({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=go(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:mo(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 Bs(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_liveHum(e){return this._config?.show_temperature&&e.primary&&fo(this.hass,e.primary)?vo(this.hass,e.entities):null}_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,_v(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}_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(e.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const s=vp(i.tapAction,i.primary?.split(".")[0]),n=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===s){const e=this._toggleIntent(i);if(!e)return;if(!$p(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=$p(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=wn(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:s}=t;this._startDevicePressFeedback(i.id),this.hass.callService(s.domain,s.service,s.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},s=((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 n=this._toggleConfirmationLines(e);if(!n.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:s}),lines:n,initialIntent:e,deviceId:i.id,exec:()=>{const s=this._devices.find(e=>e.id===i.id),n=s?this._toggleIntent(s):null;n&&!$p(n)&&this._showUnavailableToggleTargets(n)||(n&&Mp(e,n)?t(n):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===s)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===s){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),s=this.hass.states[e];if(!t||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||e;return void n(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"===s&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||$p(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Sp.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Sp.has(s.reason))continue;const e=String(s.name||s.entityId||s.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 s=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),n=Number.isFinite(s)?Math.max(.95,Math.min(1,s)):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(n)},{scale:"0.95",offset:.5},{scale:"1"}],a=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,a);const r=()=>{this._devicePressAnimations.get(e)===a&&this._devicePressAnimations.delete(e)};a.addEventListener("finish",r,{once:!0}),a.addEventListener("cancel",r,{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 la(ra(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){const t=`${e}.aria`,i=ra(this.hass,this._config?.language);return ca(i,e)&&ca(i,t)?W``:G}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 s=this._pos(i);t.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,s=Math.cos(i)*e.rlen/2,n=Math.sin(i)*e.rlen/2,o=qh([[e.rx-s,e.ry-n],[e.rx+s,e.ry+n]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){const s=qh("line"===e.kind?[[e.x1*cv,e.y1*i],[e.x2*cv,e.y2*i]]:"text"===e.kind?[[e.x*cv,e.y*i]]:jr({x:e.x*cv,y:e.y*i,w:e.w*cv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=qh(i);e&&t.push(e)}}return jh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:cv,h:cv};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 s=Kh(this._contentItems(e));let n=s.all||Yh(e),o=this._showFar?n:s.core||Yh(e);return t&&t.id===e.id&&i&&t.grow&&(o=dv(t.rect,o),n=dv(t.all,n)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,outliers:s.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),s=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})),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=`${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=_f(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=_f(t,-1)}else t=_f(t,e?1:0);else for(const i of e)t=_f(t,i.codePointAt(0)||0);else t=_f(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:s,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:tf,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?Ec(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,o):null;if(r&&("failed-core"===r.status||"not-applicable"===r.status))throw new Error("wall boolean geometry failed");const l=r?.paperGeom??zc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||Bc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?zp(i.partitionHost,s):t.length||"gate"===e.type?Uc(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:s}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(mf([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 s;if("passage"===e.type)s=[];else if("gate"===e.type){const n=10*e.face.side;s=[ff(e,0,[-i,0],[i,0],n,0,.88*t),ff(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[ff(e,0,[-i,0],[i,0],-90,.27*t,.78*t),ff(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[ff(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(s.map(e=>Object.freeze(e)))}}({...e,face:o},n)});return{walls:r?.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,s=e.build(),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=function(e){let t=1/0,i=1/0,s=-1/0,n=-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),s=Math.max(s,e.hinge[0]+o),n=Math.max(n,e.hinge[1]+o)}return mf([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=rf({rect:a?dv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=tf,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const s=[],n=[],o=[];let a=0;for(let r=0;r<(e||[]).length;r++){const l=e[r];for(let e=0;e<(l||[]).length;e++){const c=hf(l[e],e>0);if(!(c.length<3||Math.abs(cf(c))<1e-9)){s.push(df(c,t,i)),a+=c.length;for(let s=0;se.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:s.join(" "),sides:n,contactPath:o.join(" "),edgeCount:a}}(s.walls,tf,n),c={geometry:l,floor:uf(s.floor,o),openings:s.openings,frame:r};return Xf(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,s=this._isoFallback,"iso"===t&&i&&!s.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,s}_scenePoint(e){return"iso"===this._renderProjection?nf(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=of([e.x,e.y]),i=of([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=rf({rect:this._frameOf().rect,wallHeight:wh(64,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._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -4983,7 +4983,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 style="left:${r.toFixed(1)}%;top:${l.toFixed(1)}%" @click=${e=>{e.stopPropagation(),this._fitAll()}}> - `}_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:Ni(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 s=t.clientHeight+(i?.getBoundingClientRect().height||0);return s<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,s).w}_screenToVb(e,t){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+e/n*s.w,s.y+t/o*s.h]}_clampView(e,t){const i=(e,t,i,s)=>{const n=1*Math.max(t,s),o=i-n,a=i+s-t+n;return Math.max(Math.min(o,a),Math.min(Math.max(o,a),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){const s=this._baseVb(),n=Ni(s,this._stageAspect()),o=Math.min(fv.ZOOM_MAX,Math.max(fv.ZOOM_MIN,e)),a=n.w/o,r=n.h/o,l=this._viewOr(s),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-a/2,y:h-r/2,w:a,h:r},n);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 s=t-this._bootStart;s>=1200||s>=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},Vf)}_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)return;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,s=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,n=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._view||this._applyView(this._zoom));s?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 s=this._view;n>=bd?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._applyView(this._zoom,s?s.x+s.w/2:void 0,s?s.y+s.h/2:void 0)})))}_zoomAt(e,t,i){const s=this._stageEl;if(!s)return;const n=Ni(this._baseVb(),this._stageAspect()),o=Math.min(fv.ZOOM_MAX,Math.max(fv.ZOOM_MIN,i));"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1));const a=s.clientWidth,r=s.clientHeight,l=this._screenToVb(e,t),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-e/a*c,y:l[1]-t/r*h,w:c,h:h},n)}_onWheel(e){const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),s=e.deltaY<0?1.15:1/1.15;this._zoomAt(e.clientX-i.left,e.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(e){const t=this._stageEl;t&&(this._zoomAt(t.clientWidth/2,t.clientHeight/2,this._zoom*(e>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const e=this._baseVb();"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=1,this._view=Ni(e,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(sv,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){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){if(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._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)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 t=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:t.x,vy:t.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){"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],s=i[1]-t.a[1],n=Math.max(Math.abs(e),Math.abs(s));i=this._snap([t.a[0]+(e<0?-n:n),t.a[1]+(s<0?-n:n)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId)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){"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(),s=(e[0].x+e[1].x)/2-i.left,n=(e[0].y+e[1].y)/2-i.top;this._zoomAt(s,n,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._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");const s=this._stageEl;if("pan"===this._panLock&&s){const e=this._baseVb(),n=this._viewOr(e),o=Ni(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.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){this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const t=this._swipeStart;if(this._swipeStart=null,t&&t.id===e.pointerId){const i=e.clientX-t.x,s=e.clientY-t.y;if(Math.abs(i)+Math.abs(s)<8){const e=Date.now();e-this._lastTap<350&&this._resetZoom(),this._lastTap=e}const n="pan"===this._panLock?null:function(e,t,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(e)e.id),this._space);n&&this._slideTo(n,i<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);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,setTimeout(()=>this._suppressClick=!1,0)),t&&0===this._pointers.size&&this.requestUpdate()}_clickRoom(e){!this._suppressClick&&e.area&&_v("/config/areas/area/"+e.area)}_pointerDown(e,t){if("plan"===this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,s=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,s),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind)return;e.preventDefault();const i=this._pos(t);this._drag={id:t.id,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1},gv(e),this._tip=null}_pointerMove(e,t){if(!this._drag||this._drag.id!==t.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),n=i.getBoundingClientRect(),o=this._viewOr(s),a=(e.clientX-this._drag.sx)/n.width*o.w,r=(e.clientY-this._drag.sy)/n.height*o.h;Math.abs(e.clientX-this._drag.sx)+Math.abs(e.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=Oh(this._drag.ox+a),c=Oh(this._drag.oy+r);this._savePos(t,l,c)}_pointerUp(e,t){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==t.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=t.id,window.setTimeout(()=>this._drag=null,0))}_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(rv))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(rv)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(rv))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),"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,s=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),n=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-n.left,(t.y+i.y)/2-n.top,this._pinchStart.zoom*s),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._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,s,n,o){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:s,temp:n,hum:o}))}get _gridPitch(){return zh}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,s){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*s}(e,t,this._gridPitch,this._cellCm);return li(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,lv}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return pi(e?.rooms||[]).map(e=>[e[0]*lv,e[1]*t,e[2]*lv,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(nv)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(nv,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,"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._settingsDialog=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){this._endTabDrag(),this._pendingNavMode=null;const i=this._spaceModel();if(!i)return this._cancelModeTransition(!1),void(this._mode="view");if(this._kiosk&&"view"!==e)return;if(this._mode===e){if(!t&&"view"===e&&this._modeTransitionBusy){const t=this._currentModeVisual(e),i=this._modeTransitionTargetZoom,s=this._modeTransitionTargetCenterX,n=this._modeTransitionTargetCenterY;this._cancelModeTransition(!1),this._commitViewModeAtomic(t,i,s,n)}return}if(this._bootSoftCancel(),("plan"===e||"decor"===e)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._clearTransientHover(!0),this._cancelDevicePressFeedback();const s=this._mode,n=this._effectiveProjection(),o=this._currentModeVisual(s),a=this._modeTransitionBusy,r=o&&"view"!==s&&"view"!==e?{...o,toolbarContentOpacity:this._reducedMotion?1:.35}:o;this._cancelModeTransition(!1),this._editorSecondary.closeForNavigation(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const l=!i.bg&&"view"===e!=("view"===s),c=a&&"view"===s&&"view"!==e?this._modeTransitionEditorCamera:null;let h=c?.zoom??(a?this._modeTransitionTargetZoom:this._zoom),d=c?c.centerX:a?this._modeTransitionTargetCenterX:r?.viewport.centerX,p=c?c.centerY:a?this._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===s&&"view"!==e&&!a){const t=this._view;if(this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?this._logicalViewCenter(n)?.x:void 0,cy:t?this._logicalViewCenter(n)?.y:void 0},"iso"===n){const t=this._logicalViewCenter("iso");d=t?.x,p=t?.y,this._view=null,this._mode=e,this._applyView(this._zoom,t?.x,t?.y)}l&&(h=1,d=void 0,p=void 0)}if("plan"===s&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=e,this._editorChromeMode="view"===e?s:e,"view"===e){"view"===s||a||(this._modeTransitionEditorCamera={zoom:this._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const e=this._viewModeSnap;if(e&&e.space===this._space){h=e.zoom;const t=this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat",i=null!=e.cx&&null!=e.cy&&"iso"===t?sf([e.cx,e.cy],0):null;d=i?.[0]??e.cx,p=i?.[1]??e.cy}else e&&(h=this._zoomBySpace[this._space]||1,d=void 0,p=void 0)}else this._modeTransitionEditorCamera={zoom:h,centerX:d,centerY:p};this._modeTransitionTargetZoom=h,this._modeTransitionTargetCenterX=d,this._modeTransitionTargetCenterY=p,("iso"===n||"view"===e&&this._labsIso&&"iso"===this._viewPreference[this._space])&&(this._modeTransitionForceAtomic=!0);const u=++this._modeTransitionRequest;t?r?(this._modeTransitionPreparing=!0,this._modeTransitionVisual=r,this._prepareModeTransition(u,r,e,h,d,p)):(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._zoom=h,this._view=null,requestAnimationFrame(()=>{this.isConnected&&u===this._modeTransitionRequest&&this._mode===e&&(this._applyView(h,d,p),"view"===e&&(this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate())})):"view"===e&&this._commitViewModeAtomic(r,h,d,p),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszResetController(),this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===e&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=bl(ul,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?bl(ul,this._imperial):this._drawWallField}get _drawWallCm(){const e=Kf(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Yc:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Yc:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){this._showToast(this._t("toast.physical_range",{min:bl(t,this._imperial),max:bl(e,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(e=this._curSpaceCfg){return(e?.room_drafts||[]).reduce((e,t)=>e+(Array.isArray(t.segments)?t.segments.length:0),0)}_mergeSpacePartitions(e,t){const i=e?.partitions||[];if(i.length<2)return 0;const s=cm(i,{pitch:Fh,seedIds:t,geometry:dm(e,{excludeDraftId:this._activeDraftId})});return s.merged?(e.partitions=s.partitions,hm(e.openings,e.partitions,s.openingMoves,{coordScale:lv,cellCm:this._cellCm,gridPitch:this._gridPitch}),s.merged):0}_finishWallChain(){if("draw"!==this._tool||this._wallFaceBatch||this._roomDialog)return!0;const e=this._curSpaceCfg;if(!e||this._path.length<2)return this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),!0;const t=su(this._path,iu(this._path.length-1,this._draftSegmentCms,this._drawWallCm,ul)),i=t.length;if((e.partitions||[]).length+i>pv)return this._showToast(this._t("toast.physical_limit")),!1;const s=this._geometrySnapshot();e.partitions||=[];const n=Date.now().toString(36),o=[],a=this._activeDraftId?(e.room_drafts||[]).find(e=>e.id===this._activeDraftId):null,r=this._draftSegmentsForPath(this._path,a,t.map(e=>e.cm));for(let s=0;se.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),this._commitPhysicalGeometry(this._t("history.wall_chain_finish"),s),delete this._resumeDraftBySpace[this._space],this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(e){e!==this._tool&&(this._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this._tool&&(this._resize.dragging?this._rszCancelDrag():this._resize.reset()),this._tool=e,"draw"===e&&this._resumeLastDraft(),"resize"===e&&this._resize.selectRoom(null),"wallthick"===e&&(this._wallDialog=null)))}_limitReached(e){const t=this._curSpaceCfg;if(!t)return!0;const i="draft"===e?(t.room_drafts||[]).length>=200:"partition"===e?(t.partitions||[]).length>=pv:(t.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(e){const t=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(e.clientX-t.left,e.clientY-t.top)}_snap(e){const t=this._gridPitch;return[Oh(ri(e[0],t)),Oh(ri(e[1],t))]}_snapDrawPoint(e,t=!1){const i=this._path[this._path.length-1],s=t&&i?function(e,t,i,s=1/0){if(!(i>0&&e?.every(Number.isFinite)&&t?.every(Number.isFinite)))return[t[0],t[1]];const n=t[0]-e[0],o=t[1]-e[1];if(Math.abs(n)+Math.abs(o)<=1e-12)return[e[0],e[1]];const a=Math.PI/4;let r=Math.atan2(o,n);r<0&&(r+=2*Math.PI);const l=Math.floor(r/a+.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 p=Math.max(0,Math.round((n*c+o*h)/(i*d)));if(Number.isFinite(s)&&s>=0){const t=(e,t)=>{if(!t)return 1/0;const n=t>0?s-e:e+s;return Math.max(0,Math.floor((n+1e-9*i)/i))};p=Math.min(p,t(e[0],c),t(e[1],h))}return[e[0]+c*p*i,e[1]+h*p*i]}(i,e,this._gridPitch,Rh):e;return this._snap(s)}_planSnapOpeningCuts(e,t){if(!this._openingsR.length)return[];const i=this._openingWallIndexFor(e,t).value,s=[];for(const t of this._roomWallOpeningInputs(this._openingsR,e)){const e=jc(i,t);if(!e.negative&&!e.positive)continue;const n=t.angle*Math.PI/180,o=Math.cos(n)*t.length/2,a=Math.sin(n)*t.length/2;s.push([t.x-o,t.y-a,t.x+o,t.y+a])}return s}_planSnapGeometrySnapshot(){const e=this._spaceModel();if(!e)return{key:`${this._space}|empty`,value:{segments:[],endpoints:[]}};const t=[this._space,this._cfgEpoch,this._activeDraftId||"",e.rooms.length,e.room_drafts.length,e.partitions.length].join("|");if(this._planSnapGeometryCache?.key===t)return this._planSnapGeometryCache;const i=this._openCuts(),s=Kp({space:e,activeDraftId:this._activeDraftId,roomCuts:this._planSnapOpeningCuts(e,i),partitionCuts:this._partitionOpeningCuts(e),epsilon:2e-4*this._gridPitch});return this._planSnapGeometryCache={key:t,value:s},this._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const e=this._spaceModel();if(!e)return{key:`${this._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const t=["hidden",this._space,this._cfgEpoch,this._activeDraftId||"",e.rooms.length,e.room_drafts.length,e.partitions.length].join("|");if(this._hiddenWallDiagnosticCache?.key===t)return this._hiddenWallDiagnosticCache;const i=Vp({space:e,activeDraftId:this._activeDraftId,epsilon:2e-4*this._gridPitch});return this._hiddenWallDiagnosticCache={key:t,value:i},this._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const e=this._spaceModel();if(!e)return{key:`${this._space}|structural-empty`,value:{segments:[],endpoints:[]}};const t=["structural",this._space,this._cfgEpoch,this._activeDraftId||"",e.rooms.length,e.room_drafts.length,e.partitions.length].join("|");if(this._planStructuralGeometryCache?.key===t)return this._planStructuralGeometryCache;const i=Kp({space:e,activeDraftId:this._activeDraftId,epsilon:2e-4*this._gridPitch});return this._planStructuralGeometryCache={key:t,value:i},this._planStructuralGeometryCache}_planSnapContextKey(e){const t=this._path[0],i=this._path[this._path.length-1];return[e,this._tool,this._path.length,t?`${t[0]},${t[1]}`:"",i?`${i[0]},${i[1]}`:""].join("|")}_resolvePlanDrawPoint(e,t){const i=this._planSnapGeometrySnapshot(),s=this._path[this._path.length-1],n="draw"===this._tool&&this._path.length>=3?[{point:this._path[0],key:"closure:first-point"}]:[],o={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this._gridPitch,excludePoints:s?[s]:[],extraEndpoints:n,epsilon:2e-4*this._gridPitch},a=t&&s?eu(i.value,e,{...o,anchor:s}):function(e,t,i){if(!(Hp(t)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??Op,n=i.excludePoints||[],o=Zp(Xp(e,t,i),i.distinguishTolerance||0);if(o)return o;let a=null;for(const o of e.segments){const e=[o.a[0],o.a[1],o.b[0],o.b[1]],r=Hs([t[0],t[1]],e);if(r>i.tolerance)continue;const l=Qp(o,t,i.gridStep);!Yp(l,n,s)&&Jp(r,o.key,a)&&(a={kind:"line",point:l,key:o.key,distance:r,segment:o})}return a?{kind:"resolved",candidate:a,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(i.value,e,o),r="resolved"===a.kind?a.candidate:null,l=r?[...r.point]:this._snapDrawPoint(e,t),c=s?ua(s,l):l;return{point:c,candidate:r&&fi(c,r.point)?r:null,conflicts:"ambiguous"===a.kind?a.conflicts:[],ambiguous:"ambiguous"===a.kind,contextKey:this._planSnapContextKey(i.key)}}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){this._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),e&&(this._cursorPt=null)}_samePt(e,t){return fi(e,t)}_dropLegacySegments(){for(const e of this._serverCfg?.markers||[])"ripple"===e.display&&(e.display="icon_ripple");for(const e of this._serverCfg?.spaces||[]){delete e.segments;const t=new Set,i=e=>"string"==typeof e&&e.length>=1&&e.length<=64&&!t.has(e),s=e=>(t.add(e),!0),n=e=>Array.isArray(e)&&2===e.length&&e.every(e=>Number.isFinite(Number(e))&&Math.abs(Number(e))<=Ih);if(Array.isArray(e.partitions)&&(e.partitions=e.partitions.filter(e=>e&&i(e.id)&&n(e.a)&&n(e.b)&&Math.hypot(e.a[0]-e.b[0],e.a[1]-e.b[1])>1e-9&&s(e.id)).map(e=>({...e,cm:Number.isFinite(Number(e.cm))?Math.max(0,Math.min(100,Number(e.cm))):15})),e.partitions.length||delete e.partitions),Array.isArray(e.wall_columns)&&(e.wall_columns=e.wall_columns.filter(e=>e&&i(e.id)&&n(e.center)&&s(e.id)).map(e=>({id:e.id,shape:"circle"===e.shape?"circle":"square",center:[Number(e.center[0]),Number(e.center[1])],cm:Xc(Number(e.cm)||15),..."circle"===e.shape?{}:{angle:Zc(e.angle)}})),e.wall_columns.length||delete e.wall_columns),Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e&&i(e.id)&&Array.isArray(e.points)&&e.points.length>=2&&e.points.every(n)&&s(e.id)).map(e=>{const t=Lm(e);return{id:e.id,...t}}).filter(e=>e.points.length>=2),e.room_drafts.length||delete e.room_drafts),Array.isArray(e.walls)){const t=Td(Th({spaces:[e]}),e.id),i=t?w_(e,t,lv,.02*zh).contour.map(e=>e.map(e=>e/lv)):vd(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]);e.walls=Fl(e.walls,e.rooms||[],Fh,1,i),e.walls.length||delete e.walls}}}_rollbackRejectedPhysicalWrites(e){if(!this._serverCfg||!e.length)return!1;let t=!1;for(const[i]of e){const e=this._pendingPhysicalWrites.get(i);e&&(t=this._restoreGeometryStateInConfig(this._serverCfg,e.before)||t,this._pendingPhysicalWrites.delete(i))}return!!t&&(this._clearGeometryGesture(),this._geometryHistory.clear(),this._cfgEpoch++,this._modelCache=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._frame=null,this._regSignature="",this._cfgContentFingerprint=Md(this._serverCfg),this._maybeRebuildDevices(),this.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const e=this._writeChain;await e.catch(()=>{}),await this._reloadConfigOnly(!0)}get _cfgWriting(){return this._writesPending>0}_writeConfig(){var e,t;this._writesPending++,this._writeChain=(e=this._writeChain,t=async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const e=pl(Xr(this._serverCfg)),t=Md(e),i=[...this._pendingPhysicalWrites.entries()];for(const[t,s]of i){const i=mg(e.spaces.find(e=>e.id===t));if(i===s.fingerprint)continue;let n=!1;try{n=this._checkSpacePhysicalGeometry(e,t).ok}catch{n=!1}if(!n)throw this._restoreGeometryStateLocal(s.before),this._pendingPhysicalWrites.delete(t),this._geometryHistory.clear(),this._showToast(this._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});s.fingerprint=i}let s;t!==Md(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t;try{s=await this.hass.callWS({type:"houseplan/config/set",config:e,expected_rev:this._cfgRev})}catch(e){if(!this._rollbackRejectedPhysicalWrites(i))throw e;const t=e&&"object"==typeof e?e:Object.assign(new Error(String(e)),{cause:e});throw t.physicalGeometryRolledBack=!0,t}this._cfgRev=s?.rev??this._cfgRev+1;for(const[e,t]of i)this._pendingPhysicalWrites.get(e)?.fingerprint===t.fingerprint&&this._pendingPhysicalWrites.delete(e)},e.catch(()=>{}).then(t));return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){const i=e?.spaces?.find(e=>e.id===t);if(!i)return null;const s=e=>JSON.parse(JSON.stringify(e)),n={};for(const e of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==i[e]&&(n[e]=i[e]);return{spaceId:t,rooms:s(i.rooms||[]),...Array.isArray(i.openings)?{openings:s(i.openings)}:{},...Array.isArray(i.walls)?{walls:s(i.walls)}:{},...Array.isArray(i.wall_segments)?{wall_segments:s(i.wall_segments)}:{},...Array.isArray(i.open_spans)?{open_spans:s(i.open_spans)}:{},...Array.isArray(i.room_drafts)?{room_drafts:s(i.room_drafts)}:{},...Array.isArray(i.partitions)?{partitions:s(i.partitions)}:{},...Array.isArray(i.wall_columns)?{wall_columns:s(i.wall_columns)}:{},...Array.isArray(i.decor)?{decor:s(i.decor)}:{},plan_transform:n}}_geometrySnapshot(e=this._space){return this._geometrySnapshotFromConfig(this._serverCfg,e)}_recordGeometry(e,t){if(!t)return;const i=this._geometrySnapshot(t.spaceId);i&&JSON.stringify(t)!==JSON.stringify(i)&&(this._geometryHistory.push({name:e,before:t,after:i}),this.requestUpdate())}_restoreGeometryStateInConfig(e,t,i=!1){const s=e?.spaces?.find(e=>e.id===t.spaceId);if(!s)return!1;const n=e=>JSON.parse(JSON.stringify(e)),o=new Map((s.rooms||[]).map(e=>[e.id,e]));if(s.rooms=n(t.rooms),i)for(const e of s.rooms){const t=o.get(e.id);!Array.isArray(e.wall_ids)&&Array.isArray(t?.wall_ids)&&t.wall_ids.length===e.poly?.length&&(e.wall_ids=n(t.wall_ids))}const a=(e,t)=>{void 0!==t?s[e]=n(t):i&&"wall_segments"===e||delete s[e]},r=new Map((s.openings||[]).map(e=>[e.id,e])),l=new Map((s.room_drafts||[]).map(e=>[e.id,e]));if(a("openings",t.openings),a("walls",t.walls),a("wall_segments",t.wall_segments),a("open_spans",t.open_spans),a("room_drafts",t.room_drafts),a("partitions",t.partitions),a("wall_columns",t.wall_columns),a("decor",t.decor),i){for(const e of s.openings||[]){const t=r.get(e.id);e.host||"wall"!==t?.host?.kind||(e.host=n(t.host))}for(const e of s.room_drafts||[]){const t=l.get(e.id);for(let i=0;i<(e.segments||[]).length;i++)!e.segments[i]?.id&&t?.segments?.[i]?.id&&(e.segments[i].id=t.segments[i].id)}}for(const e of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete s[e];return Object.assign(s,n(t.plan_transform||{})),!0}_restoreGeometryStateLocal(e){return!!this._restoreGeometryStateInConfig(this._serverCfg,e)&&(this._cfgEpoch++,this._modelCache=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._frame=null,this.requestUpdate(),!0)}_wallModelBlockerLabel(e){const t=e instanceof Rm?e.reason:"invalid-room";return this._t(`wall_model.reason.${t}`)}_hasLegacyZeroWallFields(e=this._serverCfg){return(e?.spaces||[]).some(e=>Array.isArray(e?.open_spans)&&e.open_spans.length>0||(e?.rooms||[]).some(e=>Array.isArray(e?.open_to)&&e.open_to.length>0))}_showWallModelMigrationBlocked(e){const t=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this._showToast(this._t(t,{reason:this._wallModelBlockerLabel(e)}))}_commitPhysicalGeometry(e,t,i=[]){if(!t||!this._serverCfg)return!1;const s=this._serverCfg,n=this._geometrySnapshotFromConfig(s,t.spaceId),o=s.spaces.find(e=>e.id===t.spaceId);if(!o||mg(t)===mg(o))return!1;let a,r=!1;try{r=this._checkSpacePhysicalGeometry(s,t.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(t),this._showToast(this._t("toast.geometry_unsafe")),!1;let l=t;try{if(Number(s.model_version||0)<9){const e=JSON.parse(JSON.stringify(s));if(!this._restoreGeometryStateInConfig(e,t))throw new Rm("invalid-room",t.spaceId);const i=sg(e).config;l=this._geometrySnapshotFromConfig(i,t.spaceId)||t;const o=JSON.parse(JSON.stringify(i));if(!n||!this._restoreGeometryStateInConfig(o,n,!0))throw new Rm("invalid-room",t.spaceId);const r=i.spaces.find(e=>e.id===t.spaceId),c=o.spaces.find(e=>e.id===t.spaceId);if(!Array.isArray(n.wall_segments)&&JSON.stringify(t.rooms)===JSON.stringify(n.rooms))for(const e of c?.wall_segments||[])e.cm=zl(n.walls,e.a,e.b,Fh,1);const h=((e,t,i)=>{const s=new Map,n=new Map((e?.rooms||[]).map(e=>[e.id,e])),o=new Map((t||[]).map(e=>[e.id,e])),{rooms:a}=Zm(i,Jm(e));for(const e of a){const t=o.get(e.id),a=n.get(e.id),r=hi(t),l=hi((i.rooms||[]).find(t=>t.id===e.id)),c=hi(a);if(r&&l&&c&&r.length===l.length&&c.length===a?.wall_ids?.length&&e.poly.length===e.wall_ids.length)for(let t=0;t{const n=[];for(let o=0;oIm||Wm(r,i,s)>Im)continue;const l=qm(a,i,s),c=qm(r,i,s);l<-Im||c>1+Im||c<=l+Im||n.push({start:l,id:t[o]})}return n.sort((e,t)=>e.start-t.start)},n=i(c,a.wall_ids,r[t],r[(t+1)%r.length]),o=i(e.poly,e.wall_ids,l[t],l[(t+1)%l.length]);if(n.length&&n.length===o.length)for(let e=0;ee.id===t.spaceId),s=this._path.length>=2?this._path.map(e=>[e[0]/lv,e[1]/lv]):[];s.push(...i.map(e=>[e[0],e[1]])),c=Bm(e)<=Bm(l,s)&&this._checkSpacePhysicalGeometry(a,t.spaceId).ok}catch{c=!1}if(!c)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(t),this._showToast(this._t("toast.geometry_unsafe")),!1;og(s,a),this._recordGeometry(e,l);const h=s.spaces.find(e=>e.id===t.spaceId),d=this._pendingPhysicalWrites.get(t.spaceId);return this._pendingPhysicalWrites.set(t.spaceId,{before:d?.before||l,fingerprint:mg(h)}),this._saveConfig(),!0}_clearGeometryGesture(){this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),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._rszResetController(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(e){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===e.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid===e.pointerId||this._physicalRotate?.pid===e.pointerId)return void this._cancelPhysicalGesture();if(this._decorDraft?.pid===e.pointerId)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove?.pid===e.pointerId||this._dtDrag?.pid===e.pointerId||this._bdDrag?.pid===e.pointerId)return void this._cancelDecorGesture();"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();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),t&&0===this._pointers.size&&this.requestUpdate()}_applyGeometryState(e,t=!1){if(!this._canCommitSpace(e.spaceId))return!1;const i=this._geometrySnapshot(e.spaceId);if(!i||!this._restoreGeometryStateLocal(e))return!1;const s=this._serverCfg,n=mg(i)!==mg(e);if(n){let n=!1;try{const i=s?this._checkSpacePhysicalGeometry(s,e.spaceId):null;n=!!i?.ok||!(!t||"wall-degraded-extra"!==i?.reason)}catch{n=!1}if(!n)return this._restoreGeometryStateLocal(i),this._showToast(this._t("toast.geometry_unsafe")),!1}let o;try{o=sg(s).config}catch(e){return this._restoreGeometryStateLocal(i),this._showWallModelMigrationBlocked(e),!1}if(n){let n=!1;try{const i=o?this._checkSpacePhysicalGeometry(o,e.spaceId):null;n=!!i?.ok||!(!t||"wall-degraded-extra"!==i?.reason)}catch{n=!1}if(!n)return this._restoreGeometryStateLocal(i),this._showToast(this._t("toast.geometry_unsafe")),!1;og(s,o);const a=s?.spaces.find(t=>t.id===e.spaceId),r=this._pendingPhysicalWrites.get(e.spaceId);this._pendingPhysicalWrites.set(e.spaceId,{before:r?.before||i,fingerprint:mg(a)})}else og(s,o);return this._clearGeometryGesture(),this._space!==e.spaceId&&(this._commitSpace(e.spaceId),this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(e){return this._spaceModel()?.rooms.find(t=>{const i=hi(t);return!!i&&wi(e,i)})}_overlapRoom(e){return this._spaceModel()?.rooms.find(t=>{const i=hi(t);return!!i&&Di(e,i)})}_pointInRoom(e,t){return t.poly?vi(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){if(!Ra(e))return!0;const t=e.length,i=.001;for(let s=0;s=500)return this._showToast(this._t("toast.physical_limit")),!1;const e=this._curSpaceCfg;return!(!this._activeDraftId&&(e?.room_drafts||[]).length>=200||this._draftSegmentCount(e)>=2e3||(e?.partitions||[]).length+this._path.length>pv)||(this._showToast(this._t("toast.physical_limit")),!1)}_markupClick(e){if(this._vacFit)return;if(!this._markup)return;const t=this._spaceModel();if(!t)return void this._clearGeometryGesture();if(this._suppressClick)return;if(this._drag||this._rlResize)return;const i=e.composedPath?.()||[];if(i.some(e=>e?.classList?.contains?.("roomlabel")||e?.classList?.contains?.("rlhandle")))return;if(i.some(e=>e?.classList?.contains?.("physical-hit")))return;const s=this._svgPoint(e);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._resize.dragging||i.some(e=>e?.classList?.contains?.("rszhandle")))return;const e=[...t.rooms].reverse().find(e=>this._pointInRoom(s,e));return this._resize.selectRoom(e?.id||null),void this.requestUpdate()}if("delroom"===this._tool)return void this._deleteRoomClick(s);if("opening"===this._tool)return void this._openingClick(s);if("merge"===this._tool)return void this._mergeClick(s);if("wallthick"===this._tool)return void this._wallThickClick(s);if("split"===this._tool)return void this._splitClick(s);if("column"===this._tool)return void this._columnClick(s);this._wallRepairDiagnostic=null;const n=this._resolvePlanDrawPoint(s,e.shiftKey);if(n.ambiguous)return this._planSnapHover={contextKey:n.contextKey,candidate:null,conflicts:n.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(n.conflicts),void this._showToast(this._t("toast.plan_snap_ambiguous"));let o=n.point;if(e.ctrlKey||e.metaKey){if(e.preventDefault(),this._path.length<3)return;o=ua(this._path[this._path.length-1],this._path[0])}if(!this._path.length){if(!e.shiftKey&&!n.candidate&&this._offerExistingWallFace(s))return;const t=this._draftEndAt(o);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(e=>[...e]):t.draft.points.map(e=>[...e]),void(this._draftSegmentCms=this._adoptDraftCms(this._path,t.reverse?[...t.draft.segments].reverse().map(e=>e.cm):t.draft.segments.map(e=>e.cm),t.draft.id))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[o]))}const a=this._path[this._path.length-1];if(this._samePt(o,a))return;const r=this._draftEndAt(o,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const c=this._path.map(e=>[...e]);this._path=[...this._path,o],this._draftSegmentCms=[...this._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(c)}_draftEndAt(e,t){const i=this._spaceModel();if(!i)return null;const s=this._viewOr(this._baseVb()),n=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?s.w/this._stageEl.clientWidth*12:0);for(const s of i.room_drafts||[]){if(s.id===t)continue;if(s.points.length<2)continue;const i=s.points[0],o=s.points[s.points.length-1];if(Math.hypot(e[0]-o[0],e[1]-o[1])<=n)return{draft:s,reverse:!1};if(Math.hypot(e[0]-i[0],e[1]-i[1])<=n)return{draft:s,reverse:!0}}return null}_mergeDraftEndpoint(e){const t=this._curSpaceCfg;if(!t||!this._path.length)return;const i=this._path.map(e=>[...e]),s=this._wallGraphSources(i),n=Array.isArray(t.room_drafts)?t.room_drafts:[],o=this._activeDraftId?n.find(e=>e.id===this._activeDraftId):null,a=n.find(t=>t.id===e.draft.id);if(!a)return;const r=e.reverse?e.draft.points.map(e=>[...e]):[...e.draft.points].reverse().map(e=>[...e]),l=e.reverse?(a.segments||[]).map(e=>({...e})):[...a.segments||[]].reverse().map(e=>({...e})),c=this._path[this._path.length-1],h=this._samePt(c,r[0]),d=h?null:this._drawWallCm;if(!h&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this._path,o,this._draftSegmentCms),u=[...this._path.map(e=>[...e]),...h?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._showToast(this._t("toast.physical_limit"));const v=(o?.segments?.length||0)+(a.segments?.length||0);if(this._draftSegmentCount(t)-v+f.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(m){const e=g.slice(0,-1);if(this._contourSelfIntersects(e)||Ti(e)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(e);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const y=this._geometrySnapshot(),b=this._activeDraftId||e.draft.id,w={...o||a,id:b,points:g.map(e=>[e[0]/lv,e[1]/lv]),segments:f};t.room_drafts=n.filter(t=>t.id!==e.draft.id&&(!this._activeDraftId||t.id!==this._activeDraftId)),t.room_drafts.push(w),this._activeDraftId=b,this._resumeDraftBySpace[this._space]=b,this._path=g,this._draftSegmentCms=this._adoptDraftCms(g,f.map(e=>Number(e.cm)),b),this._physicalSel=null,this._commitPhysicalGeometry(this._t("history.draft_merge"),y),null!=d&&this._offerWallFaces(i,i.length-1,s)}_adoptDraftCms(e,t,i){const s=Math.max(0,e.length-1),n=iu(s,t,this._drawWallCm,ul);return t.length!==s&&console.debug(`[houseplan] draft ${i??"?"}: восстановлено толщин ${s-t.length} (#234)`),n}_draftSegmentsForPath(e,t,i){const s=Array.isArray(t?.points)?t.points.map(e=>[Number(e?.[0])*lv,Number(e?.[1])*lv]):[],n=Array.isArray(t?.segments)?t.segments:[];return iu(Math.max(0,e.length-1),i,this._drawWallCm,ul).map((t,i)=>{const o=e[i],a=e[i+1],r=s.findIndex((e,t)=>{if(t+1>=s.length)return!1;const i=s[t+1];return this._samePt(o,e)&&this._samePt(a,i)||this._samePt(o,i)&&this._samePt(a,e)});return{...r>=0?n[r]:{},cm:t}})}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const e=this._geometrySnapshot(),t=this._curSpaceCfg;t.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const i=t.room_drafts.findIndex(e=>e.id===this._activeDraftId),s={...i>=0?t.room_drafts[i]:{},id:this._activeDraftId,points:this._path.map(e=>[e[0]/lv,e[1]/lv]),segments:this._draftSegmentsForPath(this._path,i>=0?t.room_drafts[i]:null,this._draftSegmentCms)};i>=0?t.room_drafts[i]=s:t.room_drafts.push(s),this._commitPhysicalGeometry(this._t("history.draft_segment"),e)}_activeWallSourceKey(e){return`active:${this._activeDraftId||"session"}:${e}`}_wallGraphSources(e){const t=this._planStructuralGeometrySnapshot().value.segments.map(e=>({a:e.a,b:e.b,key:`static:${e.key}`}));for(let i=0;i+1e.key===i);if(s>=0){const[e]=this._wallFaceGraphCache.splice(s,1);return this._wallFaceGraphCache.push(e),e.value}const n=Su(e,t);return this._wallFaceGraphCache.push({key:i,value:n}),this._wallFaceGraphCache.length>4&&this._wallFaceGraphCache.shift(),n}_offerWallFaces(e,t=this._path.length-2,i){if(this._path.length<2||this._wallFaceBatch||this._roomDialog)return;const s=this._spaceModel();if(!s)return;const n=this._activeWallSourceKey(t),o=2e-4*this._gridPitch;let a,r;try{const t=i||this._wallGraphSources(e);r=this._wallGraphSources(this._path),a=function(e,t,i){const s=new Set(e.faces.map(e=>e.key));return t.faces.filter(e=>!s.has(e.key)&&e.sourceKeys.includes(i))}(this._wallFaceGraph(t,o),this._wallFaceGraph(r,o),n)}catch{return}if(!a.length){const e=Nu(r,{requiredSourceKey:n,maxDistance:wl(2,this._cellCm,this._gridPitch),gridStep:this._gridPitch,epsilon:o});return"ambiguous"===e.kind?(this._wallRepairDiagnostic=e.proposals[0]||null,void this._showToast(this._t(0===this._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==e.kind||this._overlapRoom(e.face.ring)||this._beginWallFaceBatch([{...e.face,repair:e.proposal}]))}for(const e of s.rooms){const t=hi(e);if(!e.id||!t)continue;const i=zi(t,this._path,.02*this._gridPitch);if(!i)continue;const[s,n]=i,o=Ti(s)>=Ti(n)?s:n,r=o===s?n:s,l=[...a].sort((e,t)=>Math.abs(e.area-Ti(r))-Math.abs(t.area-Ti(r))||e.key.localeCompare(t.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(e=>[e[0],e[1]]),split:{roomId:e.id,mainPoly:o,newPoly:r},consumeAllActive:!0}])}const l=a.filter(e=>!(e.ring.length<3||this._contourSelfIntersects(e.ring))&&!this._overlapRoom(e.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(e){this._wallRepairDiagnostic=null,this._wallFaceBatch={candidates:[...e].sort((e,t)=>e.area-t.area||e.key.localeCompare(t.key)),index:0,decisions:[],activePath:this._path.map(e=>[...e]),activeCms:[...this._draftSegmentCms],activeDraftId:this._activeDraftId},this._clearPlanSnapHover(),this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0}_offerExistingWallFace(e){if(this._path.length||this._wallFaceBatch||this._roomDialog)return!1;const t=2e-4*this._gridPitch;try{let i,s=Mu(this._wallFaceGraph(this._wallGraphSources([]),t),e,t);if(!s){const n=Nu(this._wallGraphSources([]),{point:e,maxDistance:wl(2,this._cellCm,this._gridPitch),gridStep:this._gridPitch,epsilon:t});if("ambiguous"===n.kind)return this._wallRepairDiagnostic=n.proposals[0]||null,this._showToast(this._t(0===this._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===n.kind)s=n.face,i=n.proposal;else if("none"===n.kind){const i=Nu(this._wallGraphSources([]),{point:e,maxDistance:this._cssPxToRender(12),gridStep:this._gridPitch,epsilon:t});if("repair"===i.kind)return this._wallRepairDiagnostic=i.proposal,this._showToast(this._t("toast.wall_repair_too_large")),!0}}return!(!s||s.ring.length<3||this._contourSelfIntersects(s.ring)||this._overlapRoom(s.ring))&&(this._beginWallFaceBatch([{...s,existing:!i,repair:i}]),!0)}catch{return!1}}_columnClick(e){const t=this._snap(e),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Yc,1);if(!this._curSpaceCfg||this._limitReached("column"))return;const s=this._spaceModel();if(!s)return;const n={id:"column-"+Date.now().toString(36),shape:"square",center:t,cm:Xc(i),angle:0},o=(s.wall_columns||[]).find(e=>vh(e,n,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const a=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...n,center:[t[0]/lv,t[1]/lv]}),this._commitPhysicalGeometry(this._t("history.column_add"),a)}_openPhysicalDialog(e,t,i){const s=this._spaceModel();if(s)if("partition"===e){const i=s.partitions.find(e=>e.id===t);i&&(this._physicalDialog={kind:e,id:t,cm:bl(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===e){const i=s.wall_columns.find(e=>e.id===t);i&&(this._physicalDialog={kind:e,id:t,cm:bl(i.cm,this._imperial),shape:i.shape,angle:this._angleField("square"===i.shape?Zc(i.angle):0)})}else{const n=s.room_drafts.find(e=>e.id===t),o=Math.max(0,Math.min(n?.segments.length?n.segments.length-1:0,i||0));n?.segments[o]&&(this._physicalDialog={kind:e,id:t,segment:o,cm:bl(n.segments[o].cm,this._imperial),length:this._fmtLen(n.points[o],n.points[o+1])})}}_physicalDown(e,t,i){const s=this._spaceModel();if(!s)return;e.stopPropagation(),gv(e);const n=this._svgPoint(e),o=[];for(const e of[...s.wall_columns].reverse())gh(n,nh(e,this._cellCm,this._gridPitch))&&o.push({kind:"column",id:e.id});for(const e of[...s.partitions].reverse()){const t=sh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);t&&gh(n,t)&&o.push({kind:"partition",id:e.id})}o.some(e=>e.kind===t&&e.id===i)||o.unshift({kind:t,id:i});const a=o.map(e=>`${e.kind}:${e.id}`).sort().join("|"),r=performance.now(),l=this._physicalPickCycle,c=o.length>1&&l?.signature===a&&r-l.at>380&&r-l.at<=1200&&Math.hypot(e.clientX-l.x,e.clientY-l.y)<=10?(l.index+1)%o.length:Math.max(0,o.findIndex(e=>e.kind===t&&e.id===i));this._physicalPickCycle={signature:a,index:c,x:e.clientX,y:e.clientY,at:r},t=o[c].kind,i=o[c].id;const h="partition"===t?s.partitions.find(e=>e.id===i):s.wall_columns.find(e=>e.id===i);h&&(this._physicalSel={kind:t,id:i},this._physicalDrag={pid:e.pointerId,kind:t,id:i,start:this._svgPoint(e),startClient:[e.clientX,e.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(h)),delta:[0,0]})}_clampPhysicalDelta(e,t,i){const s="partition"===e?[t.a,t.b]:[t.center],n=s.map(e=>e[0]),o=s.map(e=>e[1]);return[Math.max(-Ih*lv-Math.min(...n),Math.min(Ih*lv-Math.max(...n),i[0])),Math.max(-Ih*lv-Math.min(...o),Math.min(Ih*lv-Math.max(...o),i[1]))]}_physicalMove(e){const t=this._physicalDrag;if(!t||t.pid!==e.pointerId)return;e.stopPropagation();const i=this._svgPoint(e),s="partition"===t.kind?t.base.a:t.base.center,n=this._snap([s[0]+i[0]-t.start[0],s[1]+i[1]-t.start[1]]),o=this._clampPhysicalDelta(t.kind,t.base,[n[0]-s[0],n[1]-s[1]]);this._physicalDrag={...t,delta:o,moved:t.moved||Math.hypot(e.clientX-t.startClient[0],e.clientY-t.startClient[1])>=5}}_physicalUp(e){const t=this._physicalDrag;if(!t||t.pid!==e.pointerId)return;if(e.stopPropagation(),this._physicalDrag=null,!t.moved)return void this._registerPhysicalTap(t.kind,t.id);const i=this._spaceModel();if(!this._curSpaceCfg||!i)return;const s=this._curSpaceCfg;if("partition"===t.kind){const e=(s.partitions||[]).find(e=>e.id===t.id),i=t.base;if(e){const n={...i,a:[i.a[0]+t.delta[0],i.a[1]+t.delta[1]],b:[i.b[0]+t.delta[0],i.b[1]+t.delta[1]]};e.a=[n.a[0]/lv,n.a[1]/lv],e.b=[n.b[0]/lv,n.b[1]/lv];for(const e of s.openings||[]){if("partition"!==e.host?.kind||e.host.id!==t.id)continue;const i=Tp(e,[n],lv,this._cellCm,this._gridPitch).resolved;i&&Object.assign(e,Np(e,i,lv))}}}else{const e=(s.wall_columns||[]).find(e=>e.id===t.id),n=t.base,o={...n,center:[n.center[0]+t.delta[0],n.center[1]+t.delta[1]]};if(i.wall_columns.some(e=>e.id!==t.id&&vh(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));e&&(e.center=[(n.center[0]+t.delta[0])/lv,(n.center[1]+t.delta[1])/lv])}this._commitPhysicalGeometry(this._t("history.physical_move"),t.before)}_registerPhysicalTap(e,t,i){const s=performance.now(),n=this._physicalLastTap?.kind===e&&this._physicalLastTap.id===t&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:e,id:t,segment:i,at:s},n&&(this._physicalLastTap=null,this._openPhysicalDialog(e,t,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(e,t){if("square"!==t.shape||!this._spaceModel())return;e.preventDefault(),e.stopPropagation(),gv(e);const i=this._svgPoint(e);this._physicalSel={kind:"column",id:t.id},this._physicalRotate={pid:e.pointerId,id:t.id,center:[...t.center],startAngle:180*Math.atan2(i[1]-t.center[1],i[0]-t.center[0])/Math.PI,baseAngle:Zc(t.angle),angle:Zc(t.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(e){const t=this._physicalRotate;if(!t||t.pid!==e.pointerId)return;e.preventDefault(),e.stopPropagation();const i=this._svgPoint(e),s=180*Math.atan2(i[1]-t.center[1],i[0]-t.center[0])/Math.PI,n=t.baseAngle+s-t.startAngle,o=Zc(e.shiftKey?n:5*Math.round(n/5));this._physicalRotate={...t,angle:o,moved:t.moved||Math.abs(n-t.baseAngle)>=.5}}_physicalRotateUp(e){const t=this._physicalRotate;if(!t||t.pid!==e.pointerId)return;e.preventDefault(),e.stopPropagation(),this._physicalRotate=null;const i=this._spaceModel();if(!t.moved||!this._curSpaceCfg||!i)return;const s=i.wall_columns.find(e=>e.id===t.id);if(!s||"square"!==s.shape)return;const n={...s,angle:t.angle};if(i.wall_columns.some(e=>e.id!==t.id&&vh(e,n,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(e=>e.id===t.id);o&&(o.angle=t.angle,this._commitPhysicalGeometry(this._t("history.physical_edit"),t.before))}_rszRooms(){const e=[],t=this._spaceModel();if(!t)return e;for(const i of t.rooms){const t=i.id?hi(i):null;t&&e.push({id:i.id,poly:t,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0})}return i=e,s=Math.max(1e-12,1e-12*this._gridPitch),i.map(e=>{if(!Array.isArray(e.wall_ids)||e.wall_ids.length!==e.poly.length)return{...e,poly:e.poly.map(e=>[...e])};const t=e.poly.map(e=>[...e]);let i=!0;for(;i&&t.length>3;){i=!1;for(let e=0;e0){t.splice(e,1),i=!0;break}}}return{...e,poly:t}});var i,s}_rszOpenings(){return this._openingsR.map(e=>({id:e.id,x:e.rx,y:e.ry,length:e.rlen,hosted:!!e.host,angle:e.angle,type:e.type}))}_rszObstacles(){const e=this._spaceModel();if(!e)return[];const t=[];for(const i of e.partitions||[])t.push({kind:"segment",a:[...i.a],b:[...i.b],half:wl(i.cm,this._cellCm,this._gridPitch)/2});for(const i of e.room_drafts||[])for(let e=0;e+1Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2).map(e=>({wall:e,a:[e.a[0]*lv,e.a[1]*this._spaceH],b:[e.b[0]*lv,e.b[1]*this._spaceH]})),n=Math.abs(e[0]-t[0])<=.05*this._gridPitch?"v":Math.abs(e[1]-t[1])<=.05*this._gridPitch?"h":null,o=s.filter(i=>!!n&&("h"===n?!(Math.abs(i.a[1]-e[1])>.05*this._gridPitch||Math.abs(i.b[1]-e[1])>.05*this._gridPitch)&&Math.min(Math.max(e[0],t[0]),Math.max(i.a[0],i.b[0]))-Math.max(Math.min(e[0],t[0]),Math.min(i.a[0],i.b[0]))>.05*this._gridPitch:!(Math.abs(i.a[0]-e[0])>.05*this._gridPitch||Math.abs(i.b[0]-e[0])>.05*this._gridPitch)&&Math.min(Math.max(e[1],t[1]),Math.max(i.a[1],i.b[1]))-Math.max(Math.min(e[1],t[1]),Math.min(i.a[1],i.b[1]))>.05*this._gridPitch)),a=new Set(o.map(e=>Number(e.wall.cm)).filter(e=>e>0));return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch,step:this._gridPitch,movingHalf:i>0?wl(i,this._cellCm,this._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:a.size>1}}_rszResolution(e,t,i){const s=this._resize.snapshotIdentity||i||this._rszSnapshot(),n=`${this._space}|${this._cellCm}|${this._gridPitch}|${s}`,o=`${e}:${t}`;return this._resize.resolve(n,o,()=>{const i=[...this._resize.rooms||this._rszRooms()],s=i.find(t=>t.id===e),n=s?.poly?.[t]||[0,0],o=s?.poly?.[(t+1)%(s?.poly?.length||1)]||[0,0];return function(e,t,i,s,n){const{eps:o}=n,a=e.find(e=>e.id===i);if(!a||s<0||s>=(a.poly?.length||0)||a.poly.length<4||!Ra(a.poly))return{enabled:!1,reason:"invalid-geometry"};const r=a.poly[s],l=a.poly[(s+1)%a.poly.length],c=Ea(r,l,o);if(!c)return{enabled:!1,reason:"diagonal"};if(!La(a.poly,s,c,o))return{enabled:!1,reason:"side-angle"};if(n.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const e of n.obstacles||[])if(Wa(e,r,l,n))return{enabled:!1,reason:"duplicate-physical-wall"};const h=Da(Sa(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const t of e)if(t.id!==i)for(let e=0;e1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[i]:s},g=[i];if(1===d.length){const e=d[0];if(!Ra(e.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!La(e.room.poly,e.edge,c,o))return{enabled:!1,reason:"side-angle"};g.push(e.room.id),m[e.room.id]=e.edge}const f=[];for(const e of t)if(!(Ta([e.x,e.y],r,l)>2*o)){if(e.hosted||e.length>h+2*o)return{enabled:!1,reason:"opening-conflict"};f.push(e.id)}const v=Object.fromEntries(g.map(t=>[t,e.find(e=>e.id===t).poly.length])),y=[];for(const t of g){const i=e.find(e=>e.id===t),s=m[t],n=(s-1+i.poly.length)%i.poly.length,a=(s+1)%i.poly.length,r=Ga(e,t,n,1,o),l=Ga(e,t,a,0,o);if(!r||!l)return{enabled:!1,reason:"partial-shared"};y.push(r,l)}const b={roomId:i,edge:s,a:[...r],b:[...l],n:Ia(a.poly,s),roomIds:g,edgeByRoom:m,topology:v,movingOpeningIds:f,sideOwnership:y};if(!Qa(e,t,b,0,n))return{enabled:!1,reason:"invalid-geometry"};const w=Math.abs(Number(n.step));if(Number.isFinite(w)&&w>o){const i=[-w,w];if(!i.some(i=>Qa(e,t,b,i,n))){if(!i.some(i=>Ya(Ja(e,t,b,i),b,o)))return{enabled:!1,reason:"partial-shared"};const s={...n,obstacles:[]};return i.some(i=>Qa(e,t,b,i,s))?{enabled:!1,reason:"duplicate-physical-wall"}:t.length&&i.some(t=>Qa(e,[],b,t,n))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:b}}(i,[...this._resize.openings||this._rszOpenings()],e,t,this._rszOptsFor(n,o))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const e=null!==this._resize.preview;this._resize.reset(),e&&this._cfgEpoch++}_rszProjectPreview(e,t,i,s,n){const o=this._serverCfg?.spaces.find(e=>e.id===this._space);if(!o||!this._serverCfg)return{ok:!1,reason:"missing-context"};const a=JSON.parse(e),r={...o,rooms:a.rooms,openings:a.openings||[]};for(const e of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==a[e]?r[e]=a[e]:delete r[e];for(const e of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==a.plan_transform?.[e]?r[e]=a.plan_transform[e]:delete r[e];const l=this._spaceH;for(const[e,i]of Object.entries(t)){const t=r.rooms.find(t=>t.id===e);t&&(t.poly=i.map(e=>[e[0]/lv,e[1]/l]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[e,t]of Object.entries(i)){const i=(r.openings||[]).find(t=>t.id===e);i&&(i.x=t[0]/lv,i.y=t[1]/l)}const c=[],h=[];for(const e of s){const t=n.find(t=>t.id===e),i=r.rooms.find(t=>t.id===e);if(!t||!i?.poly)continue;const s=i.poly.map(e=>[e[0]*lv,e[1]*l]);if(t.poly.length!==s.length)continue;const o=Array.isArray(t.wall_ids)?t.wall_ids:[];for(let e=0;ee.id===i):null;n&&(n.a=[s[e][0]/lv,s[e][1]/lv],n.b=[s[(e+1)%s.length][0]/lv,s[(e+1)%s.length][1]/lv])}}if(c.length&&Array.isArray(r.walls)&&r.walls.length){const e=function(e,t,i,s,n=1,o="fixed-topology"){let a=!1;const r=Nl(e,t,i,s,n,o,()=>{a=!0});return{walls:a?(e||[]).map(e=>({...e})):r,rejected:a}}(r.walls,c,h,this._wallKeyPitch,lv,"fixed-topology");if(e.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=e.walls}const d=[];for(const e of r.rooms||[]){const t=hi(e);if(t&&!(t.length<2))for(let e=0;eJSON.stringify([e?.key,e?.cm,e?.a,e?.b]),u=new Map;for(const e of a.walls||[]){const t=p(e);u.set(t,(u.get(t)||0)+1)}const _=[];for(const e of r.walls||[]){const t=p(e),i=u.get(t)||0;i?u.set(t,i-1):_.push(e)}if(Hl(_,d,this._wallKeyPitch,lv,a.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this._space,r);return m.ok?{ok:!0,value:{preview:{space:this._space,sp:r},beforeWalls:a.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}:{ok:!1,reason:"physical-geometry"}}_rszAcceptPreview(e,t){if(this._cfgEpoch++,!e||!t)return;const i=Oc(t);if(!i)return;const s=`${this._space}|${this._cfgEpoch}|${e.sp.rooms.length}`;Object.defineProperty(i,"sourceFingerprint",{value:Md([e.sp,this._cellCm,this._gridPitch]),enumerable:!1});const n={key:s,value:i};Jf(this._wallUnionPool,s,n,8),this._wallUnionCache=n}_rszSpaceCandidateGeometry(e,t){if(!this._serverCfg)return{ok:!1,wallGeometry:null};const i={...this._serverCfg,spaces:this._serverCfg.spaces.map(i=>i.id===e?t:i)};let s=null;try{return{ok:this._checkSpacePhysicalGeometry(i,e,e=>{s=e}).ok,wallGeometry:s}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(e,t){if(!this._serverCfg)return!1;try{const i={...this._serverCfg,spaces:this._serverCfg.spaces.map(i=>i.id===e?t:i)};return this._checkSpacePhysicalGeometry(i,e).ok}catch{return!1}}_rszCandidateRenderable(e){return!!e&&e.space===this._space&&this._rszSpaceCandidateRenderable(e.space,e.sp)}_rszEdgeDown(e,t,i){if("resize"!==this._tool||this._resize.dragging)return;e.stopPropagation(),e.preventDefault();const s=this._rszRooms(),n=this._rszResolution(t,i);if(!n.enabled)return void this._showToast(this._rszReasonText(n.reason));gv(e);const o=n.plan,a=this._svgPoint(e),r=`${this._space}|${this._cfgEpoch}|${s.length}`,l=this._wallUnionCache?.key===r?this._wallUnionCache.value:null,c=this._rszSnapshot();this._resize.begin({pointerId:e.pointerId,start:[a[0],a[1]],roomId:t,plan:o,options:this._rszOptsFor(o.a,o.b),rooms:s,openings:this._rszOpenings(),snapshotIdentity:c,before:JSON.parse(c),wallUnionBefore:l})}_rszReasonText(e){return this._t(`resize.disabled.${e}`)}_rszDisabledActivate(e,t){e.stopPropagation(),e.preventDefault(),this._showToast(this._rszReasonText(t))}_rszDisabledKey(e,t){"Enter"!==e.key&&" "!==e.key||this._rszDisabledActivate(e,t)}_rszMove(e){if(!this._resize.ownsPointer(e.pointerId))return;e.stopPropagation();const t=this._svgPoint(e),i=this._resize.move({pointerId:e.pointerId,point:[t[0],t[1]],step:this._gridPitch,snap:e=>{const t=this._snap(e);return[t[0],t[1]]},project:(e,t,i,s,n)=>this._rszProjectPreview(e,t,i,s,n),publish:(e,t)=>this._rszAcceptPreview(e,t),measure:(e,t)=>this._rszEdgeLabels(e,t)});if("rejected"===i.kind)return i.notify&&this._showToast(this._t("resize.preview_failed")),void this.requestUpdate();"accepted"===i.kind&&this.requestUpdate()}_rszUp(e){if(!this._resize.ownsPointer(e.pointerId))return;e.stopPropagation();const t=this._resize.finish({pointerId:e.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:e=>this._rszCandidateRenderable(e)});if("no-op"===t.kind)return this._cfgEpoch++,void this.requestUpdate();if("rejected"===t.kind)return this._cfgEpoch++,this._showToast(this._t("resize.commit_failed")),void this.requestUpdate();const i=t.preview,s=this._serverCfg?.spaces.find(e=>e.id===i.space);s&&(s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls),Array.isArray(i.sp.wall_segments)&&(s.wall_segments=i.sp.wall_segments)),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._commitPhysicalGeometry(this._t("history.resize_room"),t.before),this.requestUpdate()}_rszCancelDrag(e){const t=this._resize.cancel(this._rszSnapshot(),e);if("no-op"!==t.kind){if(this._cfgEpoch++,t.restoreWallUnion){const e=this._spaceModel();if(e){const i=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`,s={key:i,value:t.restoreWallUnion};Jf(this._wallUnionPool,i,s,8),this._wallUnionCache=s}}this.requestUpdate()}}_rszPointerCancel(e){this._resize.ownsPointer(e.pointerId)&&(e.stopPropagation(),this._rszCancelDrag(e.pointerId))}_rszEdgeLabels(e,t,i=this._resize.rooms){const s=i,n=[],o=e.polys[t.roomId]||s?.find(e=>e.id===t.roomId).poly;if(!o||!s)return n;const a=o.length,r=this._rszInnerSpanCms(t.roomId,o,e.polys);for(const e of function(e,t){const i=e.length;return[(t-1+i)%i,(t+1)%i]}(o,t.edge)){const t=o[e],i=o[(e+1)%a],s=r?.[e];n.push({kind:"length",x:(t[0]+i[0])/2,y:(t[1]+i[1])/2,text:null==s?this._fmtLen(t,i):li(s,"mi"===this.hass?.config?.unit_system?.length),edge:{a:[t[0],t[1]],b:[i[0],i[1]]}})}const l="mi"===this.hass?.config?.unit_system?.length,c=t.roomIds,h=this._spaceWalls,d=this._physicalBodiesR(),p=this._baseVb(),u=this._view&&this._view.w>0&&this._view.h>0?this._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this._spaceModel(),v=this._config?.icon_size??2.5,y=f?Zh(v>8?2.5:v,f,u.w,this._kiosk?this._kioskScale.icon:1)*g.stageWidth/100:24,b=Math.max(10,.77*y),w=this._t("room.settings_short"),k=b*(1.457+.66*Math.max(1,w.length)*.42);for(const i of c){const o=e.polys[i]||s.find(e=>e.id===i).poly,a=h.length&&f&&this._innerRoomContour(f,i)||o,r=ir(d.length?uh(ph(a,d))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:tr(a,this._gridPitch,this._cellCm),l),c=cr({poly:o,edge:t.edgeByRoom[i],text:r,view:g,gearCenter:$i(o),gearWidthPx:k,gearHeightPx:b});n.push({kind:"area",roomId:i,x:c.anchor[0],y:c.anchor[1],text:r,placement:c})}return n}_rszInnerSpanCms(e,t,i){const s=Object.keys(i).length?Object.entries(i).map(([e,t])=>({id:e,poly:t})):this._spaceModel()?.rooms;if(!s?.length)return null;const n=this._spaceWalls;if(!n.length)return null;const o=function(e,t,i,s,n,o,a,r=1){const l=(e||[]).find(e=>e?.id===t),c=hi(l);if(!c||c.length<3)return null;const h=mc(e,t,i,s,n,o,a,r);if(!h)return c.map(()=>0);const d=4*Zl(n,r);return c.map((e,t)=>{const i=c[(t+1)%c.length],s=[(e[0]+i[0])/2,(e[1]+i[1])/2];for(let e=0;efunction(e,t,i){const s=e?.length||0;if(s<3||!Array.isArray(i)||i.length!==s)return 0;const n=(Math.trunc(t)%s+s)%s,o=e[n],a=e[(n+1)%s],r=a[0]-o[0],l=a[1]-o[1],c=Math.hypot(r,l);if(!(c>0))return 0;const h=Math.max(0,Number(i[n])||0);if(!(h>0))return c;const d=[r/c,l/c],p=ec(e,n),u=[o[0]+p[0]*h,o[1]+p[1]*h],_=t=>{const n=Math.max(0,Number(i[t])||0);if(!(n>0))return null;const a=e[t],r=e[(t+1)%s],l=r[0]-a[0],c=r[1]-a[1],h=Math.hypot(l,c);if(!(h>0))return null;const p=ec(e,t),_=ic(u,d,[a[0]+p[0]*n,a[1]+p[1]*n],[l/h,c/h]);return _?(_[0]-o[0])*d[0]+(_[1]-o[1])*d[1]:null},m=_((n-1+s)%s)??0,g=(_((n+1)%s)??c)-m;return g>0?g:0}(t,i,o)*a)}_renderResizeMeasurements(){const e=this._resize.liveLabels;if(!e?.length)return G;const t=e.filter(e=>"length"===e.kind),i=e.filter(e=>"area"===e.kind);return j``}_zeroWalls(){const e=this._spaceModel();return e?k_(this._curSpaceCfg,e,cv,.02*this._gridPitch):{style:v_(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){const t=this._spaceModel();if(!t)return;const i=[...t.rooms].reverse().find(t=>this._pointInRoom(e,t));i?i.id&&(this._roomDeleteDialog={roomId:i.id,name:i.name}):this._showToast(this._t("toast.delete_room_pick"))}get _wallKeyPitch(){return Fh}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_cfgOpenCuts(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return[];const i=y_(e,1),s=w_(e,t.rooms,cv,.02*this._gridPitch).map(e=>e.map(e=>e/cv)),n=new Set;return[...i,...s].filter(e=>{const t=e.map(e=>Number(e).toFixed(9)).join(","),i=[e[2],e[3],e[0],e[1]].map(e=>Number(e).toFixed(9)).join(","),s=t[e[0]/cv,e[1]/cv,e[2]/cv,e[3]/cv]))}_paperShapes(e){if(!this._spaceWalls.length)return di(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:di(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 s=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==s){const n=Jf(this._wallUnionPool,s);if(n.hit)this._wallUnionCache=n.value;else{const n=this._openCuts(),o=this._roomWallOpeningInputs(),a=Nc(e.rooms,t,n,o,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,i);a&&Object.defineProperty(a,"sourceFingerprint",{value:Md([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const r={key:s,value:a};Xf(this._wallUnionPool,s,r,8),this._wallUnionCache=r}}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 Lc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),s=this._wallUnionGeometry()?.roomGeom,n=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),a=`${e.id}|${this._cfgEpoch}|${t}|${o}`,r=Jf(this._innerContourCache,a);if(r.hit)return r.value;const l=kc(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,s,n);return Xf(this._innerContourCache,a,l,600),l}_wallThickHit(e){const t=this._spaceModel();if(!t)return null;const i=6*this._gridPitch,s=this._openCuts();let n=null;const o=(e,t,s)=>{t>i||(!n||t0?e:0,n=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return s>0?wl(s,n,o)/2:wh(1.5*o,n)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const s of e.segs)i+=(i?" ":"")+Jl([[s[0],s[1]],[s[2],s[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){const t=this._wallThickHit(e);if(!t)return void this._showToast(this._t("toast.wallthick_pick"));const i=t.cm,s=this._viewOr(this._baseVb()),n=(t.a[0]+t.b[0])/2,o=(t.a[1]+t.b[1])/2;this._wallDialog={a:t.a,b:t.b,value:bl(i,this._imperial),roomId:t.roomId,source:t.source,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(e){const t=this._wallDialog;if(!t)return;const i=this._curSpaceCfg,s=this._spaceModel();if(!i||!s)return;const n=t.value.trim(),o=n?Yf(n):null;if(null==o)return void this._showPhysicalRange(100);const a=this._imperial?2.54*o:o;if(!Number.isFinite(a)||a<0||a>100)return void this._showPhysicalRange(100);if("room"!==t.source.kind){const e=this._geometrySnapshot();if("partition"===t.source.kind){const e=(i.partitions||[]).find(e=>e.id===t.source.id);if(!e)return;if(0===a&&x_(i.openings,{kind:"partition",id:e.id}))return void this._showToast(this._t("toast.zero_wall_opening_conflict"));e.cm=a}else{const e=(i.room_drafts||[]).find(e=>e.id===t.source.id),s=e?.segments?.[t.source.segment];if(!s)return;s.cm=a}this._wallDialog=null;return this._commitPhysicalGeometry(this._t("history.wall_thickness"),e)&&this._showToast(this._t("toast.wallthick_set")),void this.requestUpdate()}const r=this._geometrySnapshot(),l=a>0?a:null;if(0===a){const s=.02*this._gridPitch,n=[t.a[0],t.a[1],t.b[0],t.b[1]],o=(i.rooms||[]).find(e=>e.id===t.roomId),a=new Set(e&&o&&Array.isArray(o.wall_ids)?o.wall_ids:(i.wall_segments||[]).filter(e=>{const t=[Number(e.a?.[0])*cv,Number(e.a?.[1])*cv],i=[Number(e.b?.[0])*cv,Number(e.b?.[1])*cv];return Ls(t,n)<=s&&Ls(i,n)<=s}).map(e=>String(e.id))),r=(i.openings||[]).some(e=>{if("partition"===e.host?.kind)return!1;if("wall"===e.host?.kind&&a.has(e.host.id))return!0;if(e.host)return!1;const t=[Number(e.x)*cv,Number(e.y)*cv];return t.every(Number.isFinite)&&Ls(t,n)<=s});if(r)return void this._showToast(this._t("toast.zero_wall_opening_conflict"))}let c,h=this._openCuts();if(a>0&&h.length){const n=[];if(e&&t.roomId){const e=(i.rooms||[]).find(e=>e.id===t.roomId),o=new Set(Array.isArray(e?.wall_ids)?e.wall_ids:[]);for(const e of i.wall_segments||[])o.has(e.id)&&n.push([Number(e.a?.[0])*cv,Number(e.a?.[1])*cv,Number(e.b?.[0])*cv,Number(e.b?.[1])*cv]);if(!n.length){const e=s.rooms.find(e=>e.id===t.roomId),i=e?hi(e):null;for(let e=0;i&&e!n.some(t=>Ls([e[0],e[1]],t)<=o&&Ls([e[2],e[3]],t)<=o&&Ls([t[0],t[1]],e)<=o&&Ls([t[2],t[3]],e)<=o))}c=e&&t.roomId?Bl(i.walls,s.rooms,t.roomId,l,this._wallKeyPitch,h,cv):Hl(i.walls,t.a,t.b,l,this._wallKeyPitch,cv),c=this._normalizeWalls(c,h);for(const e of i.wall_segments||[])e.cm=zl(c,e.a,e.b,Fh,1);c.length?i.walls=c:delete i.walls,this._wallDialog=null,this._commitPhysicalGeometry(this._t("history.wall_thickness"),r)&&this._showToast(this._t(0===a?"toast.wallthick_cleared":"toast.wallthick_set")),this.requestUpdate()}_wallHatchDefs(e){if(!this._spaceWalls.length&&!this._physicalBodiesR().length&&!this._markup)return j``;const t=fl(this._cellCm);return j` - `}_resolvedRoomFills(e,t){const i=new Map,s=new Map;for(const n of e.rooms){const e=this._roomDialog&&n.id===this._roomEditId?this._roomFill||t.fill:zs(t.fill,n),o=this._roomDialog&&n.id===this._roomEditId?this._roomCustomFill||t.customFill:ls(t.customFill,n),a=_s(e,"lqi"===e&&n.area?this._roomLqi(n.area):null,"light"===e?uo(ho(this._renderPlanHass,this._renderDevices,n,this._virtualLights)):"none","temp"===e?this._roomTemp(n):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(n,a),n.id&&s.set(n.id,a)}return{byRoom:i,byId:s}}_spaceDisplayForRender(){const e=cs(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?rs(t.customFill):as,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(";"),s=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),n=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,s,n].join("|");let a=this._openingWallIndexCache.get(o);return a||(a=Bc(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,lv)),Jf(this._openingWallIndexCache,o,a,4),{key:o,value:a}}_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 s=this._openCuts(),n=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=n.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),a=this._openingWallIndexFor(e,s),r=`${a.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===r||(this._openingTunnelCache={key:r,value:Kc(a.value,n)}),p_({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 s=new Map,n=new Map,o=this._fillColors.glow_base;for(const a of e.rooms){const e=i.byRoom.get(a),r=Fs(t.glow,a)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;s.set(a,r),a.id&&n.set(a.id,r)}return{byRoom:s,byId:n}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,hi(e)])),s=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",a=e.rooms.map(a=>{const r=t.byRoom.get(a)||null,l=i.get(a)||null;if(!r||!l)return G;const c=this._spaceWalls.length&&a.id&&this._innerRoomContour(e,a.id,s,n)||l,h=Pi(c,e.rooms.filter(e=>e!==a).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(a,c,e).path;return d||h.length?j``}_resolvedRoomFills(e,t){const i=new Map,s=new Map;for(const n of e.rooms){const e=this._roomDialog&&n.id===this._roomEditId?this._roomFill||t.fill:zs(t.fill,n),o=this._roomDialog&&n.id===this._roomEditId?this._roomCustomFill||t.customFill:ls(t.customFill,n),a=_s(e,"lqi"===e&&n.area?this._roomLqi(n.area):null,"light"===e?uo(ho(this._renderPlanHass,this._renderDevices,n,this._virtualLights)):"none","temp"===e?this._roomTemp(n):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(n,a),n.id&&s.set(n.id,a)}return{byRoom:i,byId:s}}_spaceDisplayForRender(){const e=cs(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?rs(t.customFill):as,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(";"),s=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),n=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,s,n].join("|");let a=this._openingWallIndexCache.get(o);return a||(a=Bc(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,cv)),Xf(this._openingWallIndexCache,o,a,4),{key:o,value:a}}_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 s=this._openCuts(),n=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=n.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),a=this._openingWallIndexFor(e,s),r=`${a.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===r||(this._openingTunnelCache={key:r,value:Kc(a.value,n)}),u_({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 s=new Map,n=new Map,o=this._fillColors.glow_base;for(const a of e.rooms){const e=i.byRoom.get(a),r=Fs(t.glow,a)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;s.set(a,r),a.id&&n.set(a.id,r)}return{byRoom:s,byId:n}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,hi(e)])),s=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",a=e.rooms.map(a=>{const r=t.byRoom.get(a)||null,l=i.get(a)||null;if(!r||!l)return G;const c=this._spaceWalls.length&&a.id&&this._innerRoomContour(e,a.id,s,n)||l,h=Pi(c,e.rooms.filter(e=>e!==a).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(a,c,e).path;return d||h.length?j``:a.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?vf(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=bh(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?yf(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=bh(this._cellCm);return j` ${"walls"===t&&e.materialNuance?j` @@ -5385,7 +5385,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${e.panels?j``:G} - `: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 s=hi(i);if(!s)return null;const n=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:hi(e)})).filter(e=>!!e.poly),o=Pi(s,n.map(e=>e.poly)),a=this._openCuts(),r=.02*this._gridPitch,l=a.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return s.some((e,i)=>Hs(t,[e[0],e[1],...s[(i+1)%s.length]])<=4*r)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,a,h)||s,p=[{axis:s,face:d}];for(const t of o){const i=n.find(e=>e.poly===t)?.room;let s=t;if(c.length&&i?.id){const n=mc(e.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,lv);n&&(s=Lc(n.poly,n.offsets)||t)}p.push({axis:t,face:s})}const u=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;return[e.x-i,e.y-s,e.x+i,e.y+s]}),_=l.concat(u),m=p.map(({axis:e,face:t})=>{const i=_.map(i=>((e,t,i)=>{const s=e[2]-e[0],n=e[3]-e[1],o=Math.hypot(s,n);if(o.05)&&Hs([c,h],[i[0],i[1],s[0],s[1]])<=4*r){d=!0;break}}if(!d)return null;let p=null;for(let e=0;e.05)continue;const u=Hs([c,h],[t[0],t[1],s[0],s[1]]);(!p||u!!e);return i.length?Ps(t,i,r).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(!m)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:m}}_renderRoomHoverFill(e){return e?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 s=hi(i);if(!s)return null;const n=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:hi(e)})).filter(e=>!!e.poly),o=Pi(s,n.map(e=>e.poly)),a=this._openCuts(),r=.02*this._gridPitch,l=a.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return s.some((e,i)=>Ls(t,[e[0],e[1],...s[(i+1)%s.length]])<=4*r)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,a,h)||s,p=[{axis:s,face:d}];for(const t of o){const i=n.find(e=>e.poly===t)?.room;let s=t;if(c.length&&i?.id){const n=mc(e.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,cv);n&&(s=Hc(n.poly,n.offsets)||t)}p.push({axis:t,face:s})}const u=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;return[e.x-i,e.y-s,e.x+i,e.y+s]}),_=l.concat(u),m=p.map(({axis:e,face:t})=>{const i=_.map(i=>((e,t,i)=>{const s=e[2]-e[0],n=e[3]-e[1],o=Math.hypot(s,n);if(o.05)&&Ls([c,h],[i[0],i[1],s[0],s[1]])<=4*r){d=!0;break}}if(!d)return null;let p=null;for(let e=0;e.05)continue;const u=Ls([c,h],[t[0],t[1],s[0],s[1]]);(!p||u!!e);return i.length?Ps(t,i,r).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(!m)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:m}}_renderRoomHoverFill(e){return e?j` `:j``}_renderRoomHoverOutline(e){return e?j` @@ -5410,7 +5410,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922
- `:W``}_openingAt(e){if(!this._openingsR.length)return null;const t=this._openingsR.flatMap(t=>{const i=t.angle*Math.PI/180,s=e[0]-t.rx,n=e[1]-t.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>t.rlen/2+wh(12,this._cellCm)?[]:[{o:t,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!t.length)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return t.find(({o:e,localY:t})=>{const i="gate"===e.type?!e.flip_v:e.flip_v,s=e.partitionHost||this._spaceWalls.length||"gate"===e.type?this._openingFace(e,n,!!i):{ox:0,oy:0,cm:0,side:-1},o=Uu({type:e.type,length:e.rlen,angle:e.angle,amount:this._openingAmt(e),flipH:!!e.flip_h,flipV:!!e.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s});return Math.abs(t)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(e){const t=this._openingPreset;if("opening"!==this._tool||!t)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s),o=`${n.key}|partitions:${this._cfgEpoch}`;this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===o||(this._openingPlacementIntervalsCache={key:o,value:[...gc(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,lv),...Fp(i.partitions,this._cellCm,this._gridPitch)]}),this._openingDimensionContextCache&&this._openingDimensionContextCache.key===o||(this._openingDimensionContextCache={key:o,value:Af({rooms:i.rooms,walls:this._spaceWalls,openCuts:s,partitions:i.partitions,roomOpenings:this._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(i),pitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:lv,epsilon:2e-4*this._gridPitch})});const a=$f({pointer:[e[0],e[1]],preset:t,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(t.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this._pointerModality.modality||"pen"===this._pointerModality.modality?10:6),gridStep:this._gridPitch});this._openingJambBlockCm=a.jambBlockedTarget?a.jambBlockedTarget.physicalHalfWidth/this._gridPitch*this._cellCm:null;const r=a.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let c;if(r.host){const e=Tp({id:"preview",type:r.type,x:r.x/lv,y:r.y/lv,angle:r.angle,length:r.renderedLength/lv,host:r.host},i.partitions,lv,this._cellCm,this._gridPitch).resolved;c=e?zp(e,l):{ox:0,oy:0,cm:0,side:-1}}else c=Uc(n.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const h="mi"===this.hass?.config?.unit_system?.length,d=Lf(r,this._openingDimensionContextCache.value).map(e=>({x:e.label[0],y:e.label[1],text:li(e.distance/this._gridPitch*this._cellCm,h),dimension:e}));return{...r,face:c,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(e){this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingPreset=function(e,t){return{type:e,lengthCm:bf(e),flipH:!1,flipV:!1,revision:t}}(e,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null)}_clearOpeningPlacement(e){this._openingHoverCandidate=null,this._openingJambBlockCm=null,e&&(this._openingPreset=null,this._openingRebindId=null)}_openingClick(e){const t=this._spaceModel();if(!t)return;const i=this._openingRebindId?null:this._openingAt(e);if(i)return void this._editOpening(i);const s=this._openingPreset;if(!s)return;const n=this._openingHoverCandidate,o=n&&Sf(n,[e[0],e[1]],s.revision,this._cfgEpoch)?n:this._resolveOpeningPlacement(e);if(!o){if(null!=this._openingJambBlockCm)return void this._showToast(this._t("opening.partition_jamb_margin",{distance:li(this._openingJambBlockCm,this._imperial)}));const i=1.5*this._gridPitch,s=ui(e,t.rooms,i);return s&&function(e,t,i,s,n){for(const o of s)if(!(Hs([e,t],o)>n)&&El([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),i)?void this._showToast(this._t("toast.opening_on_zero_wall")):void this._showToast(this._t("toast.opening_no_wall"))}const a=this._openingRebindId?this._curSpaceCfg?.openings?.find(e=>e.id===this._openingRebindId):null;this._openingDialog={...a?{id:a.id,type:a.type,lengthCm:Math.round(a.length*lv/this._gridPitch*this._cellCm),lengthTouched:!1,contact:a.contact||"",lock:a.lock||"",invert:!!a.invert,flipH:!!a.flip_h,flipV:!!a.flip_v}:{type:o.type,lengthCm:o.lengthCm,contact:"",lock:"",invert:!1,flipH:o.flipH,flipV:o.flipV},...o.host?{host:o.host}:{},x:o.x,y:o.y,angle:o.angle},this._openingRebindId=null,this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(e){this._openingDialog={id:e.id,type:e.type,lengthCm:Math.round(e.rlen/this._gridPitch*this._cellCm),lengthTouched:!1,contact:e.contact||"",lock:e.lock||"",invert:!!e.invert,flipH:!!e.flip_h,flipV:!!e.flip_v,..."partition"===e.host?.kind?{host:{...e.host}}:{},x:e.rx,y:e.ry,angle:e.angle}}_opPointerDown(e,t){if("plan"===this._mode&&this._spaceModel()&&"resize"!==this._tool){e.preventDefault(),e.stopPropagation();try{gv(e)}catch{}this._opDrag={id:t.id,moved:!1,sx:e.clientX,sy:e.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;if(Math.abs(e.clientX-this._opDrag.sx)+Math.abs(e.clientY-this._opDrag.sy)<=3)return;const i=this._spaceModel();if(!i)return;const s=this._svgPoint(e),n=this._curSpaceCfg,o=n?.openings?.find(e=>e.id===t.id);if(!o)return;if("partition"===o.host?.kind){const e=i.partitions.find(e=>e.id===o.host.id);if(!e)return;const t=e.b[0]-e.a[0],n=e.b[1]-e.a[1],a=Math.hypot(t,n);if(!(a>1e-9))return;const r=t/a,l=n/a;if(Math.abs((s[0]-e.a[0])*l-(s[1]-e.a[1])*r)>4*this._gridPitch)return;const c=o.length*lv/2+Dp(e,this._cellCm,this._gridPitch);let h=(s[0]-e.a[0])*r+(s[1]-e.a[1])*l;if(h=Math.round(h/this._gridPitch)*this._gridPitch,h=Math.max(c,Math.min(a-c,h)),a<2*c-1e-9)return;const d=h/a,p=(e.a[0]+r*h)/lv,u=(e.a[1]+l*h)/this._spaceH;let _=180*Math.atan2(n,t)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=o.host.t!==d||o.x!==p||o.y!==u||o.angle!==_;return this._opDrag.moved=!0,m&&(this._opDrag.dirty=!0),o.host={...o.host,t:d},o.x=p,o.y=u,o.angle=_,this._opMeasure=null,m&&this._cfgEpoch++,void this.requestUpdate()}const a=ui(s,i.rooms,4*this._gridPitch);if(!a)return;this._opDrag.moved=!0;const r=this._opRuler(a,o.length*lv);this._opMeasure=r.measure;const l=r.x/lv,c=r.y/this._spaceH,h=o.x!==l||o.y!==c||o.angle!==a.angle;if(h&&(this._opDrag.dirty=!0),o.x=l,o.y=c,o.angle=a.angle,Number(this._serverCfg?.model_version||0)>=9){const e=eg(o,n?.wall_segments||[]);e?o.host=e:delete o.host}h&&this._cfgEpoch++,this.requestUpdate()}_opRuler(e,t){const i=this._spaceModel()?.rooms||[],s=this._gridPitch/2;let n=e.x,o=e.y,a=_i([n,o],e.angle,t,i,s);if(a&&a.centered&&(n!==a.wallCenter[0]||o!==a.wallCenter[1]))[n,o]=a.wallCenter,a=_i([n,o],e.angle,t,i,s);else if(a){const[r,l]=a.wallA,[c,h]=a.wallB,d=c-r,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(t/2,u/2);let _=Math.round(((n-r)*d+(o-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),n=r+_/u*d,o=l+_/u*p,a=_i([n,o],e.angle,t,i,s)||a}}if(!a)return{x:n,y:o,angle:e.angle,measure:null};const r="mi"===this.hass?.config?.unit_system?.length,l=(e,t)=>({x:t[0],y:t[1],text:li(e/this._gridPitch*this._cellCm,r)});return{x:n,y:o,angle:e.angle,measure:{labels:[l(a.sideA,a.midA),l(a.sideB,a.midB)],guide:a.centered?{x:a.wallCenter[0],y:a.wallCenter[1],angle:e.angle}:null}}}_opPointerUp(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&this._commitPhysicalGeometry(this._t("history.move_opening"),i.before),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(e,t){"plan"===this._mode&&"resize"===this._tool||(e.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(t))}_saveOpening(){const e=this._openingDialog,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=this._geometrySnapshot(),n=this._spaceH,o=(t.openings||[]).find(t=>t.id===e.id),a={...o||{},id:e.id||"o"+Date.now().toString(36),type:e.type,x:e.x/lv,y:e.y/n,angle:e.angle,length:o&&!e.lengthTouched?o.length:this._cmToUnits(Math.max(20,e.lengthCm))/lv,...e.host?{host:{...e.host}}:{}};if("partition"===a.host?.kind){const e=Rp(o,a)?Ip(a,i.partitions,lv,this._cellCm,this._gridPitch):Tp(a,i.partitions,lv,this._cellCm,this._gridPitch);if(!e.resolved){const t=i.partitions.find(e=>e.id===a.host.id);if("does-not-fit-jamb"===e.reason&&t){const e=Dp(t,this._cellCm,this._gridPitch);this._showToast(this._t("opening.partition_jamb_margin",{distance:li(e/this._gridPitch*this._cellCm,this._imperial)}))}else this._showToast(this._t("opening.partition_orphan"));return}const s=(t.openings||[]).flatMap(e=>{if(!e.host||e.id===a.id)return[];const t=Tp(e,i.partitions,lv,this._cellCm,this._gridPitch).resolved;return t?[t]:[]});if(function(e,t,i=1e-9){const s=e.length/(2*e.axis.length),n=e.t-s,o=e.t+s;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const s=t.length/(2*t.axis.length);return Math.max(n,t.t-s)=9){const e=eg(a,t.wall_segments||[]);if(!e)return void this._showToast(this._t("toast.opening_no_wall"));a.host=e}else delete a.host;"passage"===e.type?(delete a.contact,delete a.lock,delete a.invert,delete a.flip_h,delete a.flip_v):(a.contact=e.contact||null,a.lock=("door"===e.type||"gate"===e.type)&&e.lock||null,a.invert=e.invert||void 0,a.flip_h="gate"!==e.type&&e.flipH||void 0,a.flip_v=e.flipV||void 0),t.openings=t.openings||[];const r=t.openings.findIndex(e=>e.id===a.id);r>=0?t.openings[r]=a:t.openings.push(a),this._commitPhysicalGeometry(this._t(e.id?"history.edit_opening":"history.add_opening"),s),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const e=this._openingDialog,t=this._curSpaceCfg;if(!e?.id||!t?.openings)return;const i=this._geometrySnapshot();t.openings=t.openings.filter(t=>t.id!==e.id),this._commitPhysicalGeometry(this._t("history.delete_opening"),i),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const e=[];for(const t of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(t))continue;const i=t.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[t],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&e.push([t,s?.attributes?.friendly_name||t,n?0:1])}return e.sort((e,t)=>e[2]-t[2]||e[1].localeCompare(t[1])).map(([e,t])=>({value:e,label:t}))}_lockCandidates(){return Object.keys(this.hass.states).filter(e=>e.startsWith("lock.")&&this._openingEntityAvailable(e)).map(e=>({value:e,label:this.hass.states[e]?.attributes?.friendly_name||e})).sort((e,t)=>e.label.localeCompare(t.label))}_toggleOpeningEntityPicker(e){const t=this._openingDialog;if(!t)return;const i="contact"===e?!t.contactOpen:!t.lockOpen;this._openingDialog={...t,contactOpen:"contact"===e&&i,lockOpen:"lock"===e&&i}}_filterOpeningEntities(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contactFilter:t}:{...i,lockFilter:t})}_selectOpeningEntity(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contact:t,contactOpen:!1,contactFilter:""}:{...i,lock:t,lockOpen:!1,lockFilter:""})}_mergeClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms,s=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!s?.id)return;const n=s.id;if(!this._mergeSel||this._mergeSel===n)return void(this._mergeSel=this._mergeSel===n?null:n);const o=i.find(e=>e.id===this._mergeSel),a=o?hi(o):null,r=hi(s),l=a&&r?function(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=si(Ii(e),Ii(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return s.length>=3?s:null}(a,r):null;if(!l)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:n,poly:l,pick:"a"},this._mergeSel=null}_commitMerge(){const e=this._mergeDialog,t=this._curSpaceCfg;if(!e||!t||!this._spaceModel())return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===e.pick?e.aId:e.bId,o="a"===e.pick?e.bId:e.aId,a=t.rooms.find(e=>e.id===n);if(!a)return void(this._mergeDialog=null);a.poly=e.poly.map(e=>[e[0]/lv,e[1]/s]),delete a.x,delete a.y,delete a.w,delete a.h,t.rooms=t.rooms.filter(e=>e.id!==o);const r=this._commitPhysicalGeometry(this._t("history.merge_rooms"),i);this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),r&&this._showToast(this._t("toast.rooms_merged",{name:a.name||""}))}_splitClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms;if(!this._splitSel){const t=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!t?.id)return;return void(this._splitSel={roomId:t.id,pts:[]})}const s=i.find(e=>e.id===this._splitSel.roomId),n=s?hi(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,a=6*this._gridPitch,r=function(e,t){if(!t||t.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[a+g*h,r+g*d]}return s}(r,n,this._gridPitch)||r,c=r&&l&&Math.hypot(r[0]-e[0],r[1]-e[1])<=a?l:null,h=!!c&&bi(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const t=this._snap(e);return wi(t,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,t]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=zi(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=Ti(u)>=Ti(_)?u:_,g=m===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:m,newPoly:g},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}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)return;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(e)));if("opening"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(e));const t="draw"===this._tool&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!t&&!i)return;const s=this._svgPoint(e);if(t){const t=this._resolvePlanDrawPoint(s,e.shiftKey);return this._planSnapHover={contextKey:t.contextKey,candidate:t.candidate,conflicts:t.conflicts},this._syncPlanSnapConflictMarkers(t.conflicts),this._path.length?void(this._cursorPt=t.point):void this._syncPlanSnapActiveMarker(t.candidate)}this._cursorPt=this._snap(s)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&Sf(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&(this._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(e){const t=this._wallFaceBatch;if(!t)return;const i=t.candidates[t.index];if(!i)return;if(!e&&(i.existing||i.repair)&&1===t.candidates.length)return this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",void this.requestUpdate();const s=e?{candidate:i,create:!0,name:this._nameSel.trim(),area:this._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:i,create:!1},n=[...t.decisions,s],o=t.index+1;if(o"number"==typeof e&&Number.isFinite(e)&&e>=0?e:null,n=.02*this._gridPitch,o=this._spaceModel();if(!o)return ul;for(const t of this._planSnapGeometrySnapshot().value.segments){if("room"===t.sourceKind||Hs(e,[t.a[0],t.a[1],t.b[0],t.b[1]])>n)continue;if("partition"===t.sourceKind)return s(o.partitions.find(e=>e.id===t.sourceId)?.cm)??ul;const i=t.sourceId.lastIndexOf(":"),a=i>=0?t.sourceId.slice(0,i):t.sourceId,r=i>=0?Number(t.sourceId.slice(i+1)):-1;return s(o.room_drafts.find(e=>e.id===a)?.segments[r]?.cm)??ul}const a=iu(Math.max(0,t.length-1),i,this._drawWallCm,ul);for(let i=0;i+1[...e]);if(!t?.sourceKey.startsWith("active:"))return i;const s=/:(\d+)$/.exec(t.sourceKey),n=s?Number(s[1])+("b"===t.endpoint?1:0):-1;return n>=0&&nt.key===e.sourceKey),o=s.find(t=>t.key===e.targetSourceKey);if(!n||!o)return!1;const a="a"===e.endpoint?n.a:n.b;return!(Math.hypot(a[0]-e.from[0],a[1]-e.from[1])>i)&&(!(Hs(e.to,[o.a[0],o.a[1],o.b[0],o.b[1]])>i)&&!function(e,t){if(!e.sourceKey.startsWith("static:partition|"))return!1;const i=e.sourceKey.slice(17).split("|")[0];return!!i&&t.some(e=>"partition"===e.host?.kind&&e.host.id===i)}(e,this._curSpaceCfg?.openings||[]))}_applyWallRepair(e,t){const i=this._curSpaceCfg;if(!i||!this._validateWallRepair(e,t.activePath))return!1;const s=t=>{t[0]=e.to[0]/lv,t[1]=e.to[1]/lv};if(e.sourceKey.startsWith("active:")){const n=/:(\d+)$/.exec(e.sourceKey),o=n?Number(n[1])+("b"===e.endpoint?1:0):-1;if(o<0||o>=t.activePath.length)return!1;t.activePath[o]=[...e.to];const a=(i.room_drafts||[]).find(e=>e.id===t.activeDraftId);return a?.points?.[o]&&s(a.points[o]),!0}if(!e.sourceKey.startsWith("static:"))return!1;const[n,o]=e.sourceKey.slice(7).split("|");if("partition"===n){const t=(i.partitions||[]).find(e=>e.id===o);if(!t)return!1;const n=[t.a[0]*lv,t.a[1]*lv],a=[t.b[0]*lv,t.b[1]*lv],r=Math.hypot(n[0]-e.from[0],n[1]-e.from[1])<=2e-4*this._gridPitch?t.a:Math.hypot(a[0]-e.from[0],a[1]-e.from[1])<=2e-4*this._gridPitch?t.b:null;return!!r&&(s(r),!0)}if("draft"===n){const t=o.lastIndexOf(":"),n=t>=0?o.slice(0,t):"",a=t>=0?Number(o.slice(t+1)):-1,r=(i.room_drafts||[]).find(e=>e.id===n);if(!r?.points?.[a]||!r?.points?.[a+1])return!1;const l=[a,a+1].find(t=>Math.hypot(r.points[t][0]*lv-e.from[0],r.points[t][1]*lv-e.from[1])<=2e-4*this._gridPitch);return null!=l&&(s(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const e=this._wallFaceBatch,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=(e,t)=>{this._showToast(this._t(e,t)),this._roomDialogCancel()},n=e.decisions.filter(e=>e.create),o=n.map(e=>e.candidate.ring),a=i.rooms;for(let e=0;ei.id!==t&&!!hi(i)&&Di(o[e],hi(i)));if(i)return void s("toast.room_overlap",{name:i.name||""})}if((t.rooms||[]).length+n.length>400)return void s("toast.physical_limit");if(n.some(e=>e.candidate.split&&!t.rooms.some(t=>t.id===e.candidate.split.roomId)))return void s("toast.contour_cannot_close");const r=n.map(e=>e.candidate.repair).filter(e=>!!e);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],e.activePath))return void s("toast.wall_repair_changed");const l=this._activePathWithRepair(e.activePath,r[0]),c=2e-4*this._gridPitch,h=new Map;for(let t=0;te.candidate.atomKeys)),t=n.some(e=>e.candidate.consumeAllActive);for(const i of $u(this._wallGraphSources(l),c)){const s=i.sourceKeys.find(e=>h.has(e));!s||t||e.has(i.key)||d.push({a:i.a,b:i.b,cm:h.get(s)})}}else d.push(...su(l,iu(l.length-1,e.activeCms,this._drawWallCm,ul)));if((t.partitions||[]).length+d.length>pv)return void s("toast.physical_limit");const p=n.map(e=>e.candidate.ring.map(()=>"")),u=e.activeDraftId?(t.room_drafts||[]).find(t=>t.id===e.activeDraftId):null,_=this._draftSegmentsForPath(e.activePath,u,e.activeCms),m=(e,t)=>{const i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`,s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`;return i<=s?`${i}|${s}`:`${s}|${i}`};for(let t=0;t<_.length;t++){const i=_[t]?.id;if(!i)continue;const s=e.activePath[t],o=e.activePath[t+1],a=[s[0],s[1],o[0],o[1]],r=[];n.forEach((e,t)=>e.candidate.ring.forEach((i,s)=>{const n=e.candidate.ring[(s+1)%e.candidate.ring.length];Hs(i,a)<=c&&Hs(n,a)<=c&&r.push({kind:"room",owner:t,edge:s,a:i,b:n})})),d.forEach((e,t)=>{Hs(e.a,a)<=c&&Hs(e.b,a)<=c&&r.push({kind:"partition",owner:t,edge:0,a:e.a,b:e.b})});const l=[(s[0]+o[0])/2,(s[1]+o[1])/2];r.sort((e,t)=>{const i=[e.a[0],e.a[1],e.b[0],e.b[1]],n=[t.a[0],t.a[1],t.b[0],t.b[1]];return(Hs(l,i)<=c?0:1)-(Hs(l,n)<=c?0:1)||(Hs(s,i)<=c?0:1)-(Hs(s,n)<=c?0:1)||m(e.a,e.b).localeCompare(m(t.a,t.b))});const h=r[0];if(!h)continue;const u=m(h.a,h.b);for(const e of r)m(e.a,e.b)===u&&("room"===e.kind?p[e.owner][e.edge]=i:d[e.owner].id=i)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],e))return void s("toast.wall_repair_changed");const f=n.some(e=>!!e.candidate.split),v=f?fc(i.rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,lv):null,y=f&&Array.isArray(t.walls)?t.walls:null;for(const e of n){const i=e.candidate.split;if(!i)continue;const n=t.rooms.find(e=>e.id===i.roomId);if(!n)return void s("toast.contour_cannot_close");n.poly=i.mainPoly.map(e=>[e[0]/lv,e[1]/this._spaceH]),delete n.x,delete n.y,delete n.w,delete n.h}const b=Date.now().toString(36),w=[];if(n.forEach((e,i)=>{const s=e.area?this.hass.areas[e.area]?.name:"",n={id:`r${b}-${i}`,name:e.name||s||this._t("room.default_name"),area:e.area||null,poly:e.candidate.ring.map(e=>[e[0]/lv,e[1]/this._spaceH]),...p[i].some(Boolean)?{wall_ids:p[i]}:{},...e.settings?{settings:JSON.parse(JSON.stringify(e.settings))}:{}};t.rooms.push(n),w.push({room:n,decision:e})}),e.activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==e.activeDraftId),t.room_drafts.length||delete t.room_drafts),d.length&&(t.partitions||=[],d.forEach((e,i)=>t.partitions.push({id:e.id||`partition-${b}-${i}`,a:[e.a[0]/lv,e.a[1]/lv],b:[e.b[0]/lv,e.b[1]/lv],cm:e.cm}))),f){const e=this._normalizeWalls(v,this._openCuts());e.length?t.walls=e:y?.length?t.walls=y:delete t.walls}if(w.length){this._cfgEpoch++;const i=this._openCuts(),s=this._spaceModel();if(!s)return;let n=t.walls;for(const{room:t,decision:o}of w){const a=o.candidate.ring,r=this._wallSourceCmAt(a[0],e.activePath,e.activeCms);n=ql(n,s.rooms,t.id,r,this._wallKeyPitch,i,lv);for(const o of gc(s.rooms,n,i,this._wallKeyPitch,this._cellCm,this._gridPitch,lv)){if(o.roomId!==t.id||"outer"!==o.kind)continue;const i=[(o.a[0]+o.b[0])/2,(o.a[1]+o.b[1])/2];n=Ll(n,o.a,o.b,this._wallSourceCmAt(i,e.activePath,e.activeCms),this._wallKeyPitch,lv)}}const o=this._normalizeWalls(n,i);o.length?t.walls=o:delete t.walls}if(!this._commitPhysicalGeometry(this._t("history.wall_face_batch"),g))return;delete this._resumeDraftBySpace[this._space],this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices();const k=new Set(n.map(e=>e.area).filter(e=>!!e));if(k.size){const e={...this._layout};for(const t of this._devices){if(!k.has(t.area||"")||t.space!==this._space)continue;if(this._layout[t.id])continue;const i=this._defPos[t.id];i&&(e[t.id]={s:this._space,x:i.x/lv,y:i.y/lv},this._dirtyPos.add(t.id))}this._layout=e,this._persistLayout()}this._showToast(this._t(n.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:n.length}))}_commitRoom(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return;const i=this._geometrySnapshot(),s=this._spaceH,n=!!this._pendingSplit,o=n?fc(t.rooms,e.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,lv):null,a=n&&Array.isArray(e.walls)?e.walls:null;let r;if(this._pendingSplit){const t=e.rooms.find(e=>e.id===this._pendingSplit.roomId);if(!t)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);t.poly=this._pendingSplit.mainPoly.map(e=>[e[0]/lv,e[1]/s]),delete t.x,delete t.y,delete t.w,delete t.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const l=this._areaSel?this.hass.areas[this._areaSel]?.name:"",c={id:"r"+Date.now().toString(36),name:this._nameSel||l||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(e=>[e[0]/lv,e[1]/s]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!n&&this._activeDraftId){const t=Array.isArray(e.room_drafts)?e.room_drafts.find(e=>e.id===this._activeDraftId):null,i=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),s=this._draftSegmentsForPath(this._path,t,i);s.length===r.length&&s.every(e=>!!e.id)&&(c.wall_ids=s.map(e=>e.id))}if(e.rooms.push(c),!n&&this._activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),n){const t=this._normalizeWalls(o,this._openCuts());t.length?e.walls=t:a?.length?e.walls=a:delete e.walls}if(!n){const t=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),i=t[0];if(null!=i){this._cfgEpoch++;const s=this._openCuts(),n=this._spaceModel();if(!n)return;let o=ql(e.walls,n.rooms,c.id,i,this._wallKeyPitch,s,lv);for(const e of gc(n.rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,lv)){if(e.roomId!==c.id||"outer"!==e.kind)continue;const i=[(e.a[0]+e.b[0])/2,(e.a[1]+e.b[1])/2],s=r.findIndex((e,t)=>{const s=r[(t+1)%r.length];return Hs(i,[e[0],e[1],s[0],s[1]])<=.02*this._gridPitch});s>=0&&(o=Ll(o,e.a,e.b,t[s],this._wallKeyPitch,lv))}o=this._normalizeWalls(o,s),o.length?e.walls=o:delete e.walls}}const h=this._commitPhysicalGeometry(this._t(n?"history.split_room":"history.add_room"),i);this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const d=this._areaSel;if(this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices(),!h)return;let p=0;if(d){const e=lv,t={...this._layout};for(const i of this._devices){if(i.area!==d||i.space!==this._space)continue;if(p++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(t[i.id]={s:this._space,x:s.x/lv,y:s.y/e},this._dirtyPos.add(i.id))}this._layout=t,this._persistLayout()}const u=this._model.find(e=>e.id===this._space)?.rooms.length||0;this._showToast(d?this._t("toast.room_saved",{n:u,added:p}):this._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this._wallRepairDiagnostic=null,this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),this._roomDialog=!1,this._pendingSplit=null,this._wallFaceBatch=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const e=this._resumeDraftBySpace[this._space];if(!e)return;const t=this._spaceModel()?.room_drafts.find(t=>t.id===e);t?(this._activeDraftId=e,this._path=t.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,t.segments.map(e=>e.cm),t.id),this._clearPlanSnapHover()):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){if(this._roomDialog=!1,this._roomEditId)return this._roomEditId=null,this._nameSel="",void(this._areaSel="");if(this._wallFaceBatch){const e=this._wallFaceBatch;return this._wallFaceBatch=null,this._path=e.activePath.map(e=>[...e]),this._draftSegmentCms=[...e.activeCms],this._activeDraftId=e.activeDraftId,this._nameSel="",this._areaSel="",void this.requestUpdate()}if(this._pendingSplit)return this._pendingSplit=null,void(this._splitSel=null);this._undoPoint()}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||""))}_openMarkerDialog(e){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,e&&this._ackNewDevice(e.id),this._norm)if(e){const t=e.marker,i=Object.prototype.hasOwnProperty.call(t||{},"is_light"),s=Object.prototype.hasOwnProperty.call(t||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(t||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(t||{},"value_badge"),a=Object.prototype.hasOwnProperty.call(t||{},"tap_action"),r=Object.prototype.hasOwnProperty.call(t||{},"toggle_entity"),l=ks(t?.glow_color),c=this._devicePresentation(e,!0).valueBadge,h=Wo(this._planHass,e,this._devices),d=jo(this._planHass,e,h);this._markerDialog={devId:e.id,name:e.name,binding:"virtual"===e.bindingKind?"virtual":e.bindingKind+":"+e.bindingRef,bindingMode:"virtual"===e.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===e.bindingKind&&!!this._fullRegistryHass.entities[e.bindingRef||""]?.device_id,bindingFilter:"",icon:e.marker?.icon||"",autoIcon:e.icon||"",display:Bi(e.marker?.display),rippleColor:Ie(e.marker?.ripple_color,""),rippleSize:Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):1.5,size:Number(e.marker?.size)>0?Number(e.marker.size):1,angle:Number(e.marker?.angle)||0,tapAction:vp(e.marker?.tap_action,e.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:a,originalTapAction:e.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:t?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:r,originalToggleEntity:t?.toggle_entity,tapTarget:e.marker?.tap_target||"",tapConfirm:!0===e.marker?.tap_confirm,runFilter:"",controls:Zn(e.marker?.binding,e.marker?.controls,e.entities),controlsFilter:"",lightRole:!0===t?.is_light?"always":!1===t?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:t?.is_light,lightEntity:t?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:t?.light_entity,glowMode:null!=l?.bri?"fixed":l?"color":"auto",glowColor:l?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(l?.bri??1))),glowColorDrafted:!!l,glowBrightnessDrafted:null!=l?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!l||null===t?.glow_color),originalGlowColor:l||(null===t?.glow_color?null:void 0),valueBadgeEnabled:o?!0===t?.value_badge?.enabled:!!c,valueBadgeSource:t?.value_badge?.source||c?.source||d,valueBadgePosition:t?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:t?.value_badge,useClimateTemp:!0===e.marker?.use_climate_temp,glowRadius:Number(e.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(e.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(e.marker.glow_radius_cm))/100):"",model:e.model||"",link:e.link||"",description:e.description||"",pdfs:[...e.pdfs||[]],room:e.marker?.room_id?e.space+"#@"+e.marker.room_id:e.space&&e.area?e.space+"#"+e.area:"",hideFromPlan:!0===e.marker?.hidden,busy:!1}}else this._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._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,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const e=[];for(const t of Gi)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(t+".")&&this._planEntityAvailable(i)&&e.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+t)});return e.sort((e,t)=>e.sub.localeCompare(t.sub)||e.label.localeCompare(t.label))}_bindingCandidates(){const e=this._planHass,t=wo(this._markers),i=new Set(this._markers.filter(e=>e.removed).map(e=>e.binding)),s=new Set;for(const e of this._devices)e.id!==this._markerDialog?.devId&&("device"===e.bindingKind&&e.bindingRef&&s.add("device:"+e.bindingRef),"entity"===e.bindingKind&&e.bindingRef&&s.add("entity:"+e.bindingRef));const n=new Set;for(const e of this._devices)"device"===e.bindingKind&&e.name&&n.add(e.name.trim()+"|"+(e.area||""));const o=[];for(const t of Object.values(e.devices)){if("service"===t.entry_type)continue;const e="device:"+t.id;if(s.has(e))continue;const a=(t.name_by_user||t.name||t.id).trim();e!==this._markerDialog?.binding&&!i.has(e)&&n.has(a+"|"+(t.area_id||""))||o.push({value:e,label:a,sub:(t.model||this._t("marker.sub_device"))+("Group"===t.model?this._t("marker.sub_z2m_group"):"")})}const a=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,r]of Object.entries(e.entities)){const l="entity:"+n;if(s.has(l))continue;if(ko(e,n,t)&&!i.has(l))continue;const c=a.has(r.platform),h="group"===r.platform;if(!c&&!h)continue;if(r.hidden&&!i.has(l))continue;const d=e.states[n];o.push({value:l,label:r.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===r.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(e=>e.value));for(const[a,r]of Object.entries(e.entities)){const l="entity:"+a;if(s.has(l)||n.has(l)||r.hidden&&!i.has(l))continue;const c=!!r.device_id&&t.devices.has(r.device_id);if(ko(e,a,t)&&!i.has(l)&&!c)continue;const h=e.states[a],d=r.name||h?.attributes?.friendly_name||a,p=r.device_id?e.devices[r.device_id]:null,u=p&&(p.name_by_user||p.name)||"";o.push({value:l,label:d,sub:a.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const r=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=r?o.filter(e=>(e.label+" "+e.sub+" "+e.value).toLowerCase().includes(r)):o;return l.sort((e,t)=>e.label.localeCompare(t.label)),l.slice(0,200)}_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=rh(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:uh([[[...t,t[0]]]])};const s=e.id||`#${i.rooms.indexOf(e)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._resize.preview){const e=Yf(this._cleanFloorCache,n);if(e.hit)return e.value}const o=t.map(e=>e[0]),a=t.map(e=>e[1]),r=[Math.min(...o),Math.min(...a),Math.max(...o),Math.max(...a)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=r[0]&&Math.min(...t)<=r[2]&&Math.max(...i)>=r[1]&&Math.min(...i)<=r[3]}),c=l.length?ph(t,l):null,h={floor:t,geom:c,path:c?ih(c):"",area:uh(c||[[[...t,t[0]]]])};return this._resize.preview||Jf(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(e),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===t){const e=s.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":bo(s,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=s.entities?.[i],t=this.hass.states?.[i],n=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":bo(s,n,"",[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||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(t){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===t[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},s=t[3].split(",").filter(Boolean).map(e=>i[e]?this._t(i[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||t[1],fields:s})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"").match(/margin_cm=([^;]*)/),i=t?Number(t[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:li(i,this._imperial)})}if(e.message)return e.message;if(e.error)return e.error;if(null!=e.code)return this._t("err.code",{code:e.code});try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){const t=e?.code??e?.error;if("string"==typeof t){const e=`backup.error.${t}`,i=this._t(e);if(i!==e)return i}return this._errText(e)}async _pickMarkerFiles(e){const t=e.target,i=t.files?[...t.files]:[];if(t.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const e of i)try{const t=new FormData;t.append("marker_id",s),t.append("file",e,e.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:t}):await fetch("/api/houseplan/upload",{method:"POST",body:t,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const e={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(e[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||e.name,url:o.url})}catch(t){this._showToast(this._t("toast.file_failed",{name:e.name,err:this._errText(t)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(t=>t.url!==e)})}_markerLightFields(e){const t={};if(e.lightRoleTouched?"always"===e.lightRole?t.is_light=!0:"never"===e.lightRole&&(t.is_light=!1):e.originalHasIsLight&&(t.is_light=e.originalIsLight??null),e.lightEntityTouched?e.lightEntity&&(t.light_entity=e.lightEntity):e.originalHasLightEntity&&(t.light_entity=e.originalLightEntity??null),e.glowTouched){if("auto"!==e.glowMode){const i={c:Ie(e.glowColor,this._fillColors.glow_light.c)};"fixed"===e.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(e.glowBrightness)/100))),t.glow_color=i}}else e.originalHasGlowColor&&(t.glow_color=e.originalGlowColor??null);return t}_markerTapActionFields(e){return e.tapActionTouched?{tap_action:e.tapAction||null}:e.originalHasTapAction?{tap_action:e.originalTapAction??null}:{}}_markerToggleEntityFields(e){return(t={touched:e.toggleEntityTouched,originalHas:e.originalHasToggleEntity,original:e.originalToggleEntity,value:e.toggleEntity}).touched?t.value?{toggle_entity:t.value}:{}:t.originalHas?{toggle_entity:t.original??null}:{};var t}async _saveMarker(){const e=this._markerDialog;if(!e||e.busy)return;const t=this._effectiveMarkerTapAction(e);if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return;if("virtual"===e.binding&&!e.name.trim())return void this._showToast(this._t("toast.virtual_name_required"));if("run"===t&&!e.tapTarget)return void this._showToast(this._t("toast.run_target_required"));if(e.valueBadgeTouched&&e.valueBadgeEnabled&&!e.valueBadgeSource)return void this._showToast(this._t("toast.value_badge_source_required"));if("ha"===e.bindingMode){const t=this._bindingStatus(e.binding),i=e.devId?this._markers.find(t=>t.id===e.devId):null,s=!i||i.binding!==e.binding;if("active"!==t.kind&&s)return void this._showToast(this._t("ha_disabled"===t.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===t.kind&&!0===i?.hidden&&!e.hideFromPlan)return this._markerDialog={...e,hideFromPlan:!0},void this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const i=this._serverCfg;if(!i)return;const s=i.markers||[],n=qs(e.room);let o=n?.space||null;const a=n?.area||null,r=n?.roomId||null,l=Fi(e.binding,e.devId,()=>"v_"+Date.now().toString(36)),c=e.devId,h=c?this._devices.find(e=>e.id===c):null,d=n?.space||h?.space||null,p=d?this._spaceModelById(d):this._spaceModel();if(!p)return;const u=p.id;"virtual"!==e.binding||o||(o=u),this._markerDialog={...e,busy:!0};try{const n="virtual"===e.binding?[]:s.filter(t=>t.removed&&t.binding===e.binding).map(e=>e.id),d=n.length>0,_=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),m=s.find(e=>e.id===l||e.id===c)?.vacuum||null,g={id:l,vacuum:m,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle?e.angle:null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===t&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:_.length?_:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Kf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:!!e.hideFromPlan};("virtual"===e.binding||e.room)&&(g.space=o,g.area=a,g.room_id=r);const f=h?.marker?.room_id??null,v=!!e.room&&null!=h&&(h.space!==o||h.area!==a||f!==r);let y=!1;const b=e.uploadId||c;if(b&&b!==l&&g.pdfs?.length)try{const e=await this.hass.callWS({type:"houseplan/files/migrate",from_id:b,to_id:l}),t=e?.mapping||{};g.pdfs=function(e,t,i,s){if(!t||!i||t===i)return e;const n="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(n))return e;const t=e.url.split(n)[1]||"",[i,a]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(s){const t=s[decodeURIComponent(i)]??s[i];return t?{...e,url:e.url.split(n+i)[0]+o+encodeURIComponent(t)+a}:e}return{...e,url:e.url.split(n).join(o)}})}(g.pdfs,b,l,t),y=Object.keys(t).length>0}catch(e){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(e)}))}i.markers=s,c&&c!==l&&(i.markers=function(e,t,i){if(!t||!i||t===i)return[...e];const s=`marker:${t}`,n=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===s?n:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===s?{...e.value_badge,source:{...e.value_badge.source,ref:n}}:e.value_badge;return t===e.controls&&i===e.value_badge?e:{...e,controls:t,value_badge:i}})}(i.markers,c,l)),c&&c!==l&&"derived_marker_state"===g.value_badge?.source?.kind&&g.value_badge.source.ref===`marker:${c}`&&(g.value_badge.source={kind:"derived_marker_state",ref:`marker:${l}`}),i.markers=i.markers.filter(e=>e.id!==l&&e.id!==c&&("virtual"===g.binding||e.binding!==g.binding)),i.markers.push(g);let w=null;const k=c?this._layout[c]:null,x=k?{s:k.s||h?.space||this._space,x:k.x,y:k.y}:c&&h&&this._defPos[c]?this._normPos(h.space,this._defPos[c].x,this._defPos[c].y):null;if(!d&&x&&x.s===u)l===c&&this._layout[l]&&!v||(w={s:x.s,x:x.x,y:x.y},this._layout={...this._layout,[l]:w});else if(d||!this._layout[l]||v){let e=p.vb[0]+p.vb[2]/2,t=p.vb[1]+p.vb[3]/2;const i=r?p.rooms.find(e=>e.id===r):a?p.rooms.find(e=>e.area===a):void 0;i&&([e,t]=this._roomCenter(i)),w=this._normPos(u,e,t),this._layout={...this._layout,[l]:w}}if(await this._saveConfigNow(),w){const e=hl(w);this._layout={...this._layout,[l]:e},this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:l,pos:e}))}const $=new Set(n);c&&c!==l&&$.add(c),$.delete(l);for(const e of $)delete this._layout[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{});y&&b&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:b}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _deleteMarker(){const e=this._markerDialog;if(!e||e.busy||!e.devId)return;const t=e.devId?this._devices.find(t=>t.id===e.devId):null;if(!t)return;const i=e.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===t.bindingKind?"virtual":t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:"";if(!o)return;const a=function(e,t,i,s){const n=new Set([t]),o=e.filter(e=>{const o=e.id===t||!s&&e.binding===i;return o&&n.add(e.id),!o});return{markers:s?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,t.id,o,"virtual"===t.bindingKind);var r,l;s.markers=(r=a.markers,l=a.cleanupIds,r.map(e=>{if(!Array.isArray(e.controls))return e;const t=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&l.has(e.slice(7))));return t.length===e.controls.length?e:{...e,controls:t.length?t:null}}));const c=a.cleanupIds;this._markerDialog={...e,busy:!0};try{await this._saveConfigNow();for(const e of c){delete this._layout[e],delete this._defPos[e],this._dirtyPos.delete(e),this._sentPos.delete(e);const t=this._activityRt.get(e);t&&clearTimeout(t.timer),this._activityRt.delete(e),this._vacRt.delete(e),delete this._vacSrvTrails[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:e}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:e}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===t.id&&this._closeInfoCard(),this._selId===t.id&&(this._selId=null),this._drag?.id===t.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(e){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}_normPos(e,t,i){return{s:e,x:t/lv,y:i/lv}}_openSpaceDialog(e,t){if(this._serverStorage&&this._serverCfg)if("edit"===e){const i=this._serverCfg.spaces.find(e=>e.id===t);if(!i)return;const s=cs(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?rs(i.settings.custom_fill):null,o="none"===s.fill?{...n||as,a:0}:n;this._spaceDialog={mode:e,spaceId:t,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,zeroWallStyle:f_(i),displayTouched:!0,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Pr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:xh(Number(i.cell_cm)>0?Number(i.cell_cm):5,this._imperial),cellCmTouched:!1,busy:!1}}else this._spaceDialog={mode:e,title:"",planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:{...as,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(e){const t=e.target,i=t.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let e=0;e{const t=new Image;t.onload=()=>e(t.naturalWidth&&t.naturalHeight?t.naturalWidth/t.naturalHeight:1.414),t.onerror=()=>e(1.414),t.src=r});URL.revokeObjectURL(r),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:a,aspect:l,name:i.name}}}_useServerPlan(e){const t=this._spaceDialog;t&&(this._spaceDialog={...t,planUrl:e,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(e))}async _readPlanAspect(e){for(let t=0;t<40;t++){const t=this._display(e);if(t){const i=await new Promise(e=>{const i=new Image;i.onload=()=>e(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>e(0),i.src=t}),s=this._spaceDialog;return s&&s.planUrl===e&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(e=>setTimeout(e,150)),this._spaceDialog?.planUrl!==e)return 0}return 0}async _deleteServerPlan(e){if(confirm(this._t("confirm.delete_plan",{name:e})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:e});const t=this._spaceDialog;t?.saved&&(this._spaceDialog={...t,saved:t.saved.filter(t=>t.name!==e)})}catch(e){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(e)}))}}_renderServerPlans(e){if(e.savedBusy)return W`
${this._t("space.loading")}
`;const t=e.saved||[];if(!t.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingAt(e){if(!this._openingsR.length)return null;const t=this._openingsR.flatMap(t=>{const i=t.angle*Math.PI/180,s=e[0]-t.rx,n=e[1]-t.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>t.rlen/2+wh(12,this._cellCm)?[]:[{o:t,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!t.length)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return t.find(({o:e,localY:t})=>{const i="gate"===e.type?!e.flip_v:e.flip_v,s=e.partitionHost||this._spaceWalls.length||"gate"===e.type?this._openingFace(e,n,!!i):{ox:0,oy:0,cm:0,side:-1},o=Uu({type:e.type,length:e.rlen,angle:e.angle,amount:this._openingAmt(e),flipH:!!e.flip_h,flipV:!!e.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s});return Math.abs(t)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(e){const t=this._openingPreset;if("opening"!==this._tool||!t)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s),o=`${n.key}|partitions:${this._cfgEpoch}`;this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===o||(this._openingPlacementIntervalsCache={key:o,value:[...gc(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,cv),...Fp(i.partitions,this._cellCm,this._gridPitch)]}),this._openingDimensionContextCache&&this._openingDimensionContextCache.key===o||(this._openingDimensionContextCache={key:o,value:zf({rooms:i.rooms,walls:this._spaceWalls,openCuts:s,partitions:i.partitions,roomOpenings:this._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(i),pitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:cv,epsilon:2e-4*this._gridPitch})});const a=Sf({pointer:[e[0],e[1]],preset:t,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(t.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this._pointerModality.modality||"pen"===this._pointerModality.modality?10:6),gridStep:this._gridPitch});this._openingJambBlockCm=a.jambBlockedTarget?a.jambBlockedTarget.physicalHalfWidth/this._gridPitch*this._cellCm:null;const r=a.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let c;if(r.host){const e=Tp({id:"preview",type:r.type,x:r.x/cv,y:r.y/cv,angle:r.angle,length:r.renderedLength/cv,host:r.host},i.partitions,cv,this._cellCm,this._gridPitch).resolved;c=e?zp(e,l):{ox:0,oy:0,cm:0,side:-1}}else c=Uc(n.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const h="mi"===this.hass?.config?.unit_system?.length,d=Bf(r,this._openingDimensionContextCache.value).map(e=>({x:e.label[0],y:e.label[1],text:li(e.distance/this._gridPitch*this._cellCm,h),dimension:e}));return{...r,face:c,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(e){this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingPreset=function(e,t){return{type:e,lengthCm:wf(e),flipH:!1,flipV:!1,revision:t}}(e,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null)}_clearOpeningPlacement(e){this._openingHoverCandidate=null,this._openingJambBlockCm=null,e&&(this._openingPreset=null,this._openingRebindId=null)}_openingClick(e){const t=this._spaceModel();if(!t)return;const i=this._openingRebindId?null:this._openingAt(e);if(i)return void this._editOpening(i);const s=this._openingPreset;if(!s)return;const n=this._openingHoverCandidate,o=n&&Mf(n,[e[0],e[1]],s.revision,this._cfgEpoch)?n:this._resolveOpeningPlacement(e);if(!o){if(null!=this._openingJambBlockCm)return void this._showToast(this._t("opening.partition_jamb_margin",{distance:li(this._openingJambBlockCm,this._imperial)}));const i=1.5*this._gridPitch,s=ui(e,t.rooms,i);return s&&function(e,t,i,s,n){for(const o of s)if(!(Ls([e,t],o)>n)&&El([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),i)?void this._showToast(this._t("toast.opening_on_zero_wall")):void this._showToast(this._t("toast.opening_no_wall"))}const a=this._openingRebindId?this._curSpaceCfg?.openings?.find(e=>e.id===this._openingRebindId):null;this._openingDialog={...a?{id:a.id,type:a.type,lengthCm:Math.round(a.length*cv/this._gridPitch*this._cellCm),lengthTouched:!1,contact:a.contact||"",lock:a.lock||"",invert:!!a.invert,flipH:!!a.flip_h,flipV:!!a.flip_v}:{type:o.type,lengthCm:o.lengthCm,contact:"",lock:"",invert:!1,flipH:o.flipH,flipV:o.flipV},...o.host?{host:o.host}:{},x:o.x,y:o.y,angle:o.angle},this._openingRebindId=null,this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(e){this._openingDialog={id:e.id,type:e.type,lengthCm:Math.round(e.rlen/this._gridPitch*this._cellCm),lengthTouched:!1,contact:e.contact||"",lock:e.lock||"",invert:!!e.invert,flipH:!!e.flip_h,flipV:!!e.flip_v,..."partition"===e.host?.kind?{host:{...e.host}}:{},x:e.rx,y:e.ry,angle:e.angle}}_opPointerDown(e,t){if("plan"===this._mode&&this._spaceModel()&&"resize"!==this._tool){e.preventDefault(),e.stopPropagation();try{fv(e)}catch{}this._opDrag={id:t.id,moved:!1,sx:e.clientX,sy:e.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;if(Math.abs(e.clientX-this._opDrag.sx)+Math.abs(e.clientY-this._opDrag.sy)<=3)return;const i=this._spaceModel();if(!i)return;const s=this._svgPoint(e),n=this._curSpaceCfg,o=n?.openings?.find(e=>e.id===t.id);if(!o)return;if("partition"===o.host?.kind){const e=i.partitions.find(e=>e.id===o.host.id);if(!e)return;const t=e.b[0]-e.a[0],n=e.b[1]-e.a[1],a=Math.hypot(t,n);if(!(a>1e-9))return;const r=t/a,l=n/a;if(Math.abs((s[0]-e.a[0])*l-(s[1]-e.a[1])*r)>4*this._gridPitch)return;const c=o.length*cv/2+Dp(e,this._cellCm,this._gridPitch);let h=(s[0]-e.a[0])*r+(s[1]-e.a[1])*l;if(h=Math.round(h/this._gridPitch)*this._gridPitch,h=Math.max(c,Math.min(a-c,h)),a<2*c-1e-9)return;const d=h/a,p=(e.a[0]+r*h)/cv,u=(e.a[1]+l*h)/this._spaceH;let _=180*Math.atan2(n,t)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=o.host.t!==d||o.x!==p||o.y!==u||o.angle!==_;return this._opDrag.moved=!0,m&&(this._opDrag.dirty=!0),o.host={...o.host,t:d},o.x=p,o.y=u,o.angle=_,this._opMeasure=null,m&&this._cfgEpoch++,void this.requestUpdate()}const a=ui(s,i.rooms,4*this._gridPitch);if(!a)return;this._opDrag.moved=!0;const r=this._opRuler(a,o.length*cv);this._opMeasure=r.measure;const l=r.x/cv,c=r.y/this._spaceH,h=o.x!==l||o.y!==c||o.angle!==a.angle;if(h&&(this._opDrag.dirty=!0),o.x=l,o.y=c,o.angle=a.angle,Number(this._serverCfg?.model_version||0)>=9){const e=tg(o,n?.wall_segments||[]);e?o.host=e:delete o.host}h&&this._cfgEpoch++,this.requestUpdate()}_opRuler(e,t){const i=this._spaceModel()?.rooms||[],s=this._gridPitch/2;let n=e.x,o=e.y,a=_i([n,o],e.angle,t,i,s);if(a&&a.centered&&(n!==a.wallCenter[0]||o!==a.wallCenter[1]))[n,o]=a.wallCenter,a=_i([n,o],e.angle,t,i,s);else if(a){const[r,l]=a.wallA,[c,h]=a.wallB,d=c-r,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(t/2,u/2);let _=Math.round(((n-r)*d+(o-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),n=r+_/u*d,o=l+_/u*p,a=_i([n,o],e.angle,t,i,s)||a}}if(!a)return{x:n,y:o,angle:e.angle,measure:null};const r="mi"===this.hass?.config?.unit_system?.length,l=(e,t)=>({x:t[0],y:t[1],text:li(e/this._gridPitch*this._cellCm,r)});return{x:n,y:o,angle:e.angle,measure:{labels:[l(a.sideA,a.midA),l(a.sideB,a.midB)],guide:a.centered?{x:a.wallCenter[0],y:a.wallCenter[1],angle:e.angle}:null}}}_opPointerUp(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&this._commitPhysicalGeometry(this._t("history.move_opening"),i.before),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(e,t){"plan"===this._mode&&"resize"===this._tool||(e.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(t))}_saveOpening(){const e=this._openingDialog,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=this._geometrySnapshot(),n=this._spaceH,o=(t.openings||[]).find(t=>t.id===e.id),a={...o||{},id:e.id||"o"+Date.now().toString(36),type:e.type,x:e.x/cv,y:e.y/n,angle:e.angle,length:o&&!e.lengthTouched?o.length:this._cmToUnits(Math.max(20,e.lengthCm))/cv,...e.host?{host:{...e.host}}:{}};if("partition"===a.host?.kind){const e=Rp(o,a)?Ip(a,i.partitions,cv,this._cellCm,this._gridPitch):Tp(a,i.partitions,cv,this._cellCm,this._gridPitch);if(!e.resolved){const t=i.partitions.find(e=>e.id===a.host.id);if("does-not-fit-jamb"===e.reason&&t){const e=Dp(t,this._cellCm,this._gridPitch);this._showToast(this._t("opening.partition_jamb_margin",{distance:li(e/this._gridPitch*this._cellCm,this._imperial)}))}else this._showToast(this._t("opening.partition_orphan"));return}const s=(t.openings||[]).flatMap(e=>{if(!e.host||e.id===a.id)return[];const t=Tp(e,i.partitions,cv,this._cellCm,this._gridPitch).resolved;return t?[t]:[]});if(function(e,t,i=1e-9){const s=e.length/(2*e.axis.length),n=e.t-s,o=e.t+s;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const s=t.length/(2*t.axis.length);return Math.max(n,t.t-s)=9){const e=tg(a,t.wall_segments||[]);if(!e)return void this._showToast(this._t("toast.opening_no_wall"));a.host=e}else delete a.host;"passage"===e.type?(delete a.contact,delete a.lock,delete a.invert,delete a.flip_h,delete a.flip_v):(a.contact=e.contact||null,a.lock=("door"===e.type||"gate"===e.type)&&e.lock||null,a.invert=e.invert||void 0,a.flip_h="gate"!==e.type&&e.flipH||void 0,a.flip_v=e.flipV||void 0),t.openings=t.openings||[];const r=t.openings.findIndex(e=>e.id===a.id);r>=0?t.openings[r]=a:t.openings.push(a),this._commitPhysicalGeometry(this._t(e.id?"history.edit_opening":"history.add_opening"),s),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const e=this._openingDialog,t=this._curSpaceCfg;if(!e?.id||!t?.openings)return;const i=this._geometrySnapshot();t.openings=t.openings.filter(t=>t.id!==e.id),this._commitPhysicalGeometry(this._t("history.delete_opening"),i),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const e=[];for(const t of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(t))continue;const i=t.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[t],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&e.push([t,s?.attributes?.friendly_name||t,n?0:1])}return e.sort((e,t)=>e[2]-t[2]||e[1].localeCompare(t[1])).map(([e,t])=>({value:e,label:t}))}_lockCandidates(){return Object.keys(this.hass.states).filter(e=>e.startsWith("lock.")&&this._openingEntityAvailable(e)).map(e=>({value:e,label:this.hass.states[e]?.attributes?.friendly_name||e})).sort((e,t)=>e.label.localeCompare(t.label))}_toggleOpeningEntityPicker(e){const t=this._openingDialog;if(!t)return;const i="contact"===e?!t.contactOpen:!t.lockOpen;this._openingDialog={...t,contactOpen:"contact"===e&&i,lockOpen:"lock"===e&&i}}_filterOpeningEntities(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contactFilter:t}:{...i,lockFilter:t})}_selectOpeningEntity(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contact:t,contactOpen:!1,contactFilter:""}:{...i,lock:t,lockOpen:!1,lockFilter:""})}_mergeClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms,s=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!s?.id)return;const n=s.id;if(!this._mergeSel||this._mergeSel===n)return void(this._mergeSel=this._mergeSel===n?null:n);const o=i.find(e=>e.id===this._mergeSel),a=o?hi(o):null,r=hi(s),l=a&&r?function(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=si(Ii(e),Ii(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return s.length>=3?s:null}(a,r):null;if(!l)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:n,poly:l,pick:"a"},this._mergeSel=null}_commitMerge(){const e=this._mergeDialog,t=this._curSpaceCfg;if(!e||!t||!this._spaceModel())return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===e.pick?e.aId:e.bId,o="a"===e.pick?e.bId:e.aId,a=t.rooms.find(e=>e.id===n);if(!a)return void(this._mergeDialog=null);a.poly=e.poly.map(e=>[e[0]/cv,e[1]/s]),delete a.x,delete a.y,delete a.w,delete a.h,t.rooms=t.rooms.filter(e=>e.id!==o);const r=this._commitPhysicalGeometry(this._t("history.merge_rooms"),i);this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),r&&this._showToast(this._t("toast.rooms_merged",{name:a.name||""}))}_splitClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms;if(!this._splitSel){const t=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!t?.id)return;return void(this._splitSel={roomId:t.id,pts:[]})}const s=i.find(e=>e.id===this._splitSel.roomId),n=s?hi(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,a=6*this._gridPitch,r=function(e,t){if(!t||t.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[a+g*h,r+g*d]}return s}(r,n,this._gridPitch)||r,c=r&&l&&Math.hypot(r[0]-e[0],r[1]-e[1])<=a?l:null,h=!!c&&bi(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const t=this._snap(e);return wi(t,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,t]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=zi(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=Ti(u)>=Ti(_)?u:_,g=m===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:m,newPoly:g},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}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)return;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(e)));if("opening"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(e));const t="draw"===this._tool&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!t&&!i)return;const s=this._svgPoint(e);if(t){const t=this._resolvePlanDrawPoint(s,e.shiftKey);return this._planSnapHover={contextKey:t.contextKey,candidate:t.candidate,conflicts:t.conflicts},this._syncPlanSnapConflictMarkers(t.conflicts),this._path.length?void(this._cursorPt=t.point):void this._syncPlanSnapActiveMarker(t.candidate)}this._cursorPt=this._snap(s)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&Mf(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&(this._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(e){const t=this._wallFaceBatch;if(!t)return;const i=t.candidates[t.index];if(!i)return;if(!e&&(i.existing||i.repair)&&1===t.candidates.length)return this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",void this.requestUpdate();const s=e?{candidate:i,create:!0,name:this._nameSel.trim(),area:this._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:i,create:!1},n=[...t.decisions,s],o=t.index+1;if(o"number"==typeof e&&Number.isFinite(e)&&e>=0?e:null,n=.02*this._gridPitch,o=this._spaceModel();if(!o)return ul;for(const t of this._planSnapGeometrySnapshot().value.segments){if("room"===t.sourceKind||Ls(e,[t.a[0],t.a[1],t.b[0],t.b[1]])>n)continue;if("partition"===t.sourceKind)return s(o.partitions.find(e=>e.id===t.sourceId)?.cm)??ul;const i=t.sourceId.lastIndexOf(":"),a=i>=0?t.sourceId.slice(0,i):t.sourceId,r=i>=0?Number(t.sourceId.slice(i+1)):-1;return s(o.room_drafts.find(e=>e.id===a)?.segments[r]?.cm)??ul}const a=iu(Math.max(0,t.length-1),i,this._drawWallCm,ul);for(let i=0;i+1[...e]);if(!t?.sourceKey.startsWith("active:"))return i;const s=/:(\d+)$/.exec(t.sourceKey),n=s?Number(s[1])+("b"===t.endpoint?1:0):-1;return n>=0&&nt.key===e.sourceKey),o=s.find(t=>t.key===e.targetSourceKey);if(!n||!o)return!1;const a="a"===e.endpoint?n.a:n.b;return!(Math.hypot(a[0]-e.from[0],a[1]-e.from[1])>i)&&(!(Ls(e.to,[o.a[0],o.a[1],o.b[0],o.b[1]])>i)&&!function(e,t){if(!e.sourceKey.startsWith("static:partition|"))return!1;const i=e.sourceKey.slice(17).split("|")[0];return!!i&&t.some(e=>"partition"===e.host?.kind&&e.host.id===i)}(e,this._curSpaceCfg?.openings||[]))}_applyWallRepair(e,t){const i=this._curSpaceCfg;if(!i||!this._validateWallRepair(e,t.activePath))return!1;const s=t=>{t[0]=e.to[0]/cv,t[1]=e.to[1]/cv};if(e.sourceKey.startsWith("active:")){const n=/:(\d+)$/.exec(e.sourceKey),o=n?Number(n[1])+("b"===e.endpoint?1:0):-1;if(o<0||o>=t.activePath.length)return!1;t.activePath[o]=[...e.to];const a=(i.room_drafts||[]).find(e=>e.id===t.activeDraftId);return a?.points?.[o]&&s(a.points[o]),!0}if(!e.sourceKey.startsWith("static:"))return!1;const[n,o]=e.sourceKey.slice(7).split("|");if("partition"===n){const t=(i.partitions||[]).find(e=>e.id===o);if(!t)return!1;const n=[t.a[0]*cv,t.a[1]*cv],a=[t.b[0]*cv,t.b[1]*cv],r=Math.hypot(n[0]-e.from[0],n[1]-e.from[1])<=2e-4*this._gridPitch?t.a:Math.hypot(a[0]-e.from[0],a[1]-e.from[1])<=2e-4*this._gridPitch?t.b:null;return!!r&&(s(r),!0)}if("draft"===n){const t=o.lastIndexOf(":"),n=t>=0?o.slice(0,t):"",a=t>=0?Number(o.slice(t+1)):-1,r=(i.room_drafts||[]).find(e=>e.id===n);if(!r?.points?.[a]||!r?.points?.[a+1])return!1;const l=[a,a+1].find(t=>Math.hypot(r.points[t][0]*cv-e.from[0],r.points[t][1]*cv-e.from[1])<=2e-4*this._gridPitch);return null!=l&&(s(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const e=this._wallFaceBatch,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=(e,t)=>{this._showToast(this._t(e,t)),this._roomDialogCancel()},n=e.decisions.filter(e=>e.create),o=n.map(e=>e.candidate.ring),a=i.rooms;for(let e=0;ei.id!==t&&!!hi(i)&&Di(o[e],hi(i)));if(i)return void s("toast.room_overlap",{name:i.name||""})}if((t.rooms||[]).length+n.length>400)return void s("toast.physical_limit");if(n.some(e=>e.candidate.split&&!t.rooms.some(t=>t.id===e.candidate.split.roomId)))return void s("toast.contour_cannot_close");const r=n.map(e=>e.candidate.repair).filter(e=>!!e);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],e.activePath))return void s("toast.wall_repair_changed");const l=this._activePathWithRepair(e.activePath,r[0]),c=2e-4*this._gridPitch,h=new Map;for(let t=0;te.candidate.atomKeys)),t=n.some(e=>e.candidate.consumeAllActive);for(const i of $u(this._wallGraphSources(l),c)){const s=i.sourceKeys.find(e=>h.has(e));!s||t||e.has(i.key)||d.push({a:i.a,b:i.b,cm:h.get(s)})}}else d.push(...su(l,iu(l.length-1,e.activeCms,this._drawWallCm,ul)));if((t.partitions||[]).length+d.length>uv)return void s("toast.physical_limit");const p=n.map(e=>e.candidate.ring.map(()=>"")),u=e.activeDraftId?(t.room_drafts||[]).find(t=>t.id===e.activeDraftId):null,_=this._draftSegmentsForPath(e.activePath,u,e.activeCms),m=(e,t)=>{const i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`,s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`;return i<=s?`${i}|${s}`:`${s}|${i}`};for(let t=0;t<_.length;t++){const i=_[t]?.id;if(!i)continue;const s=e.activePath[t],o=e.activePath[t+1],a=[s[0],s[1],o[0],o[1]],r=[];n.forEach((e,t)=>e.candidate.ring.forEach((i,s)=>{const n=e.candidate.ring[(s+1)%e.candidate.ring.length];Ls(i,a)<=c&&Ls(n,a)<=c&&r.push({kind:"room",owner:t,edge:s,a:i,b:n})})),d.forEach((e,t)=>{Ls(e.a,a)<=c&&Ls(e.b,a)<=c&&r.push({kind:"partition",owner:t,edge:0,a:e.a,b:e.b})});const l=[(s[0]+o[0])/2,(s[1]+o[1])/2];r.sort((e,t)=>{const i=[e.a[0],e.a[1],e.b[0],e.b[1]],n=[t.a[0],t.a[1],t.b[0],t.b[1]];return(Ls(l,i)<=c?0:1)-(Ls(l,n)<=c?0:1)||(Ls(s,i)<=c?0:1)-(Ls(s,n)<=c?0:1)||m(e.a,e.b).localeCompare(m(t.a,t.b))});const h=r[0];if(!h)continue;const u=m(h.a,h.b);for(const e of r)m(e.a,e.b)===u&&("room"===e.kind?p[e.owner][e.edge]=i:d[e.owner].id=i)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],e))return void s("toast.wall_repair_changed");const f=n.some(e=>!!e.candidate.split),v=f?fc(i.rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,cv):null,y=f&&Array.isArray(t.walls)?t.walls:null;for(const e of n){const i=e.candidate.split;if(!i)continue;const n=t.rooms.find(e=>e.id===i.roomId);if(!n)return void s("toast.contour_cannot_close");n.poly=i.mainPoly.map(e=>[e[0]/cv,e[1]/this._spaceH]),delete n.x,delete n.y,delete n.w,delete n.h}const b=Date.now().toString(36),w=[];if(n.forEach((e,i)=>{const s=e.area?this.hass.areas[e.area]?.name:"",n={id:`r${b}-${i}`,name:e.name||s||this._t("room.default_name"),area:e.area||null,poly:e.candidate.ring.map(e=>[e[0]/cv,e[1]/this._spaceH]),...p[i].some(Boolean)?{wall_ids:p[i]}:{},...e.settings?{settings:JSON.parse(JSON.stringify(e.settings))}:{}};t.rooms.push(n),w.push({room:n,decision:e})}),e.activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==e.activeDraftId),t.room_drafts.length||delete t.room_drafts),d.length&&(t.partitions||=[],d.forEach((e,i)=>t.partitions.push({id:e.id||`partition-${b}-${i}`,a:[e.a[0]/cv,e.a[1]/cv],b:[e.b[0]/cv,e.b[1]/cv],cm:e.cm}))),f){const e=this._normalizeWalls(v,this._openCuts());e.length?t.walls=e:y?.length?t.walls=y:delete t.walls}if(w.length){this._cfgEpoch++;const i=this._openCuts(),s=this._spaceModel();if(!s)return;let n=t.walls;for(const{room:t,decision:o}of w){const a=o.candidate.ring,r=this._wallSourceCmAt(a[0],e.activePath,e.activeCms);n=ql(n,s.rooms,t.id,r,this._wallKeyPitch,i,cv);for(const o of gc(s.rooms,n,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv)){if(o.roomId!==t.id||"outer"!==o.kind)continue;const i=[(o.a[0]+o.b[0])/2,(o.a[1]+o.b[1])/2];n=Hl(n,o.a,o.b,this._wallSourceCmAt(i,e.activePath,e.activeCms),this._wallKeyPitch,cv)}}const o=this._normalizeWalls(n,i);o.length?t.walls=o:delete t.walls}if(!this._commitPhysicalGeometry(this._t("history.wall_face_batch"),g))return;delete this._resumeDraftBySpace[this._space],this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices();const k=new Set(n.map(e=>e.area).filter(e=>!!e));if(k.size){const e={...this._layout};for(const t of this._devices){if(!k.has(t.area||"")||t.space!==this._space)continue;if(this._layout[t.id])continue;const i=this._defPos[t.id];i&&(e[t.id]={s:this._space,x:i.x/cv,y:i.y/cv},this._dirtyPos.add(t.id))}this._layout=e,this._persistLayout()}this._showToast(this._t(n.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:n.length}))}_commitRoom(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return;const i=this._geometrySnapshot(),s=this._spaceH,n=!!this._pendingSplit,o=n?fc(t.rooms,e.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,cv):null,a=n&&Array.isArray(e.walls)?e.walls:null;let r;if(this._pendingSplit){const t=e.rooms.find(e=>e.id===this._pendingSplit.roomId);if(!t)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);t.poly=this._pendingSplit.mainPoly.map(e=>[e[0]/cv,e[1]/s]),delete t.x,delete t.y,delete t.w,delete t.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const l=this._areaSel?this.hass.areas[this._areaSel]?.name:"",c={id:"r"+Date.now().toString(36),name:this._nameSel||l||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(e=>[e[0]/cv,e[1]/s]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!n&&this._activeDraftId){const t=Array.isArray(e.room_drafts)?e.room_drafts.find(e=>e.id===this._activeDraftId):null,i=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),s=this._draftSegmentsForPath(this._path,t,i);s.length===r.length&&s.every(e=>!!e.id)&&(c.wall_ids=s.map(e=>e.id))}if(e.rooms.push(c),!n&&this._activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),n){const t=this._normalizeWalls(o,this._openCuts());t.length?e.walls=t:a?.length?e.walls=a:delete e.walls}if(!n){const t=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),i=t[0];if(null!=i){this._cfgEpoch++;const s=this._openCuts(),n=this._spaceModel();if(!n)return;let o=ql(e.walls,n.rooms,c.id,i,this._wallKeyPitch,s,cv);for(const e of gc(n.rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,cv)){if(e.roomId!==c.id||"outer"!==e.kind)continue;const i=[(e.a[0]+e.b[0])/2,(e.a[1]+e.b[1])/2],s=r.findIndex((e,t)=>{const s=r[(t+1)%r.length];return Ls(i,[e[0],e[1],s[0],s[1]])<=.02*this._gridPitch});s>=0&&(o=Hl(o,e.a,e.b,t[s],this._wallKeyPitch,cv))}o=this._normalizeWalls(o,s),o.length?e.walls=o:delete e.walls}}const h=this._commitPhysicalGeometry(this._t(n?"history.split_room":"history.add_room"),i);this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const d=this._areaSel;if(this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices(),!h)return;let p=0;if(d){const e=cv,t={...this._layout};for(const i of this._devices){if(i.area!==d||i.space!==this._space)continue;if(p++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(t[i.id]={s:this._space,x:s.x/cv,y:s.y/e},this._dirtyPos.add(i.id))}this._layout=t,this._persistLayout()}const u=this._model.find(e=>e.id===this._space)?.rooms.length||0;this._showToast(d?this._t("toast.room_saved",{n:u,added:p}):this._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this._wallRepairDiagnostic=null,this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),this._roomDialog=!1,this._pendingSplit=null,this._wallFaceBatch=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const e=this._resumeDraftBySpace[this._space];if(!e)return;const t=this._spaceModel()?.room_drafts.find(t=>t.id===e);t?(this._activeDraftId=e,this._path=t.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,t.segments.map(e=>e.cm),t.id),this._clearPlanSnapHover()):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){if(this._roomDialog=!1,this._roomEditId)return this._roomEditId=null,this._nameSel="",void(this._areaSel="");if(this._wallFaceBatch){const e=this._wallFaceBatch;return this._wallFaceBatch=null,this._path=e.activePath.map(e=>[...e]),this._draftSegmentCms=[...e.activeCms],this._activeDraftId=e.activeDraftId,this._nameSel="",this._areaSel="",void this.requestUpdate()}if(this._pendingSplit)return this._pendingSplit=null,void(this._splitSel=null);this._undoPoint()}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||""))}_openMarkerDialog(e){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,e&&this._ackNewDevice(e.id),this._norm)if(e){const t=e.marker,i=Object.prototype.hasOwnProperty.call(t||{},"is_light"),s=Object.prototype.hasOwnProperty.call(t||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(t||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(t||{},"value_badge"),a=Object.prototype.hasOwnProperty.call(t||{},"tap_action"),r=Object.prototype.hasOwnProperty.call(t||{},"toggle_entity"),l=ks(t?.glow_color),c=this._devicePresentation(e,!0).valueBadge,h=Wo(this._planHass,e,this._devices),d=jo(this._planHass,e,h);this._markerDialog={devId:e.id,name:e.name,binding:"virtual"===e.bindingKind?"virtual":e.bindingKind+":"+e.bindingRef,bindingMode:"virtual"===e.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===e.bindingKind&&!!this._fullRegistryHass.entities[e.bindingRef||""]?.device_id,bindingFilter:"",icon:e.marker?.icon||"",autoIcon:e.icon||"",display:Bi(e.marker?.display),rippleColor:Ie(e.marker?.ripple_color,""),rippleSize:Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):1.5,size:Number(e.marker?.size)>0?Number(e.marker.size):1,angle:Number(e.marker?.angle)||0,tapAction:vp(e.marker?.tap_action,e.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:a,originalTapAction:e.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:t?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:r,originalToggleEntity:t?.toggle_entity,tapTarget:e.marker?.tap_target||"",tapConfirm:!0===e.marker?.tap_confirm,runFilter:"",controls:Zn(e.marker?.binding,e.marker?.controls,e.entities),controlsFilter:"",lightRole:!0===t?.is_light?"always":!1===t?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:t?.is_light,lightEntity:t?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:t?.light_entity,glowMode:null!=l?.bri?"fixed":l?"color":"auto",glowColor:l?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(l?.bri??1))),glowColorDrafted:!!l,glowBrightnessDrafted:null!=l?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!l||null===t?.glow_color),originalGlowColor:l||(null===t?.glow_color?null:void 0),valueBadgeEnabled:o?!0===t?.value_badge?.enabled:!!c,valueBadgeSource:t?.value_badge?.source||c?.source||d,valueBadgePosition:t?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:t?.value_badge,useClimateTemp:!0===e.marker?.use_climate_temp,glowRadius:Number(e.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(e.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(e.marker.glow_radius_cm))/100):"",model:e.model||"",link:e.link||"",description:e.description||"",pdfs:[...e.pdfs||[]],room:e.marker?.room_id?e.space+"#@"+e.marker.room_id:e.space&&e.area?e.space+"#"+e.area:"",hideFromPlan:!0===e.marker?.hidden,busy:!1}}else this._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._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,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const e=[];for(const t of Gi)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(t+".")&&this._planEntityAvailable(i)&&e.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+t)});return e.sort((e,t)=>e.sub.localeCompare(t.sub)||e.label.localeCompare(t.label))}_bindingCandidates(){const e=this._planHass,t=wo(this._markers),i=new Set(this._markers.filter(e=>e.removed).map(e=>e.binding)),s=new Set;for(const e of this._devices)e.id!==this._markerDialog?.devId&&("device"===e.bindingKind&&e.bindingRef&&s.add("device:"+e.bindingRef),"entity"===e.bindingKind&&e.bindingRef&&s.add("entity:"+e.bindingRef));const n=new Set;for(const e of this._devices)"device"===e.bindingKind&&e.name&&n.add(e.name.trim()+"|"+(e.area||""));const o=[];for(const t of Object.values(e.devices)){if("service"===t.entry_type)continue;const e="device:"+t.id;if(s.has(e))continue;const a=(t.name_by_user||t.name||t.id).trim();e!==this._markerDialog?.binding&&!i.has(e)&&n.has(a+"|"+(t.area_id||""))||o.push({value:e,label:a,sub:(t.model||this._t("marker.sub_device"))+("Group"===t.model?this._t("marker.sub_z2m_group"):"")})}const a=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,r]of Object.entries(e.entities)){const l="entity:"+n;if(s.has(l))continue;if(ko(e,n,t)&&!i.has(l))continue;const c=a.has(r.platform),h="group"===r.platform;if(!c&&!h)continue;if(r.hidden&&!i.has(l))continue;const d=e.states[n];o.push({value:l,label:r.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===r.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(e=>e.value));for(const[a,r]of Object.entries(e.entities)){const l="entity:"+a;if(s.has(l)||n.has(l)||r.hidden&&!i.has(l))continue;const c=!!r.device_id&&t.devices.has(r.device_id);if(ko(e,a,t)&&!i.has(l)&&!c)continue;const h=e.states[a],d=r.name||h?.attributes?.friendly_name||a,p=r.device_id?e.devices[r.device_id]:null,u=p&&(p.name_by_user||p.name)||"";o.push({value:l,label:d,sub:a.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const r=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=r?o.filter(e=>(e.label+" "+e.sub+" "+e.value).toLowerCase().includes(r)):o;return l.sort((e,t)=>e.label.localeCompare(t.label)),l.slice(0,200)}_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=rh(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:uh([[[...t,t[0]]]])};const s=e.id||`#${i.rooms.indexOf(e)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._resize.preview){const e=Jf(this._cleanFloorCache,n);if(e.hit)return e.value}const o=t.map(e=>e[0]),a=t.map(e=>e[1]),r=[Math.min(...o),Math.min(...a),Math.max(...o),Math.max(...a)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=r[0]&&Math.min(...t)<=r[2]&&Math.max(...i)>=r[1]&&Math.min(...i)<=r[3]}),c=l.length?ph(t,l):null,h={floor:t,geom:c,path:c?ih(c):"",area:uh(c||[[[...t,t[0]]]])};return this._resize.preview||Xf(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(e),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===t){const e=s.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":bo(s,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=s.entities?.[i],t=this.hass.states?.[i],n=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":bo(s,n,"",[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||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(t){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===t[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},s=t[3].split(",").filter(Boolean).map(e=>i[e]?this._t(i[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||t[1],fields:s})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"").match(/margin_cm=([^;]*)/),i=t?Number(t[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:li(i,this._imperial)})}if(e.message)return e.message;if(e.error)return e.error;if(null!=e.code)return this._t("err.code",{code:e.code});try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){const t=e?.code??e?.error;if("string"==typeof t){const e=`backup.error.${t}`,i=this._t(e);if(i!==e)return i}return this._errText(e)}async _pickMarkerFiles(e){const t=e.target,i=t.files?[...t.files]:[];if(t.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const e of i)try{const t=new FormData;t.append("marker_id",s),t.append("file",e,e.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:t}):await fetch("/api/houseplan/upload",{method:"POST",body:t,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const e={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(e[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||e.name,url:o.url})}catch(t){this._showToast(this._t("toast.file_failed",{name:e.name,err:this._errText(t)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(t=>t.url!==e)})}_markerLightFields(e){const t={};if(e.lightRoleTouched?"always"===e.lightRole?t.is_light=!0:"never"===e.lightRole&&(t.is_light=!1):e.originalHasIsLight&&(t.is_light=e.originalIsLight??null),e.lightEntityTouched?e.lightEntity&&(t.light_entity=e.lightEntity):e.originalHasLightEntity&&(t.light_entity=e.originalLightEntity??null),e.glowTouched){if("auto"!==e.glowMode){const i={c:Ie(e.glowColor,this._fillColors.glow_light.c)};"fixed"===e.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(e.glowBrightness)/100))),t.glow_color=i}}else e.originalHasGlowColor&&(t.glow_color=e.originalGlowColor??null);return t}_markerTapActionFields(e){return e.tapActionTouched?{tap_action:e.tapAction||null}:e.originalHasTapAction?{tap_action:e.originalTapAction??null}:{}}_markerToggleEntityFields(e){return(t={touched:e.toggleEntityTouched,originalHas:e.originalHasToggleEntity,original:e.originalToggleEntity,value:e.toggleEntity}).touched?t.value?{toggle_entity:t.value}:{}:t.originalHas?{toggle_entity:t.original??null}:{};var t}async _saveMarker(){const e=this._markerDialog;if(!e||e.busy)return;const t=this._effectiveMarkerTapAction(e);if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return;if("virtual"===e.binding&&!e.name.trim())return void this._showToast(this._t("toast.virtual_name_required"));if("run"===t&&!e.tapTarget)return void this._showToast(this._t("toast.run_target_required"));if(e.valueBadgeTouched&&e.valueBadgeEnabled&&!e.valueBadgeSource)return void this._showToast(this._t("toast.value_badge_source_required"));if("ha"===e.bindingMode){const t=this._bindingStatus(e.binding),i=e.devId?this._markers.find(t=>t.id===e.devId):null,s=!i||i.binding!==e.binding;if("active"!==t.kind&&s)return void this._showToast(this._t("ha_disabled"===t.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===t.kind&&!0===i?.hidden&&!e.hideFromPlan)return this._markerDialog={...e,hideFromPlan:!0},void this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const i=this._serverCfg;if(!i)return;const s=i.markers||[],n=qs(e.room);let o=n?.space||null;const a=n?.area||null,r=n?.roomId||null,l=Fi(e.binding,e.devId,()=>"v_"+Date.now().toString(36)),c=e.devId,h=c?this._devices.find(e=>e.id===c):null,d=n?.space||h?.space||null,p=d?this._spaceModelById(d):this._spaceModel();if(!p)return;const u=p.id;"virtual"!==e.binding||o||(o=u),this._markerDialog={...e,busy:!0};try{const n="virtual"===e.binding?[]:s.filter(t=>t.removed&&t.binding===e.binding).map(e=>e.id),d=n.length>0,_=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),m=s.find(e=>e.id===l||e.id===c)?.vacuum||null,g={id:l,vacuum:m,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle?e.angle:null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===t&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:_.length?_:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Yf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:!!e.hideFromPlan};("virtual"===e.binding||e.room)&&(g.space=o,g.area=a,g.room_id=r);const f=h?.marker?.room_id??null,v=!!e.room&&null!=h&&(h.space!==o||h.area!==a||f!==r);let y=!1;const b=e.uploadId||c;if(b&&b!==l&&g.pdfs?.length)try{const e=await this.hass.callWS({type:"houseplan/files/migrate",from_id:b,to_id:l}),t=e?.mapping||{};g.pdfs=function(e,t,i,s){if(!t||!i||t===i)return e;const n="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(n))return e;const t=e.url.split(n)[1]||"",[i,a]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(s){const t=s[decodeURIComponent(i)]??s[i];return t?{...e,url:e.url.split(n+i)[0]+o+encodeURIComponent(t)+a}:e}return{...e,url:e.url.split(n).join(o)}})}(g.pdfs,b,l,t),y=Object.keys(t).length>0}catch(e){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(e)}))}i.markers=s,c&&c!==l&&(i.markers=function(e,t,i){if(!t||!i||t===i)return[...e];const s=`marker:${t}`,n=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===s?n:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===s?{...e.value_badge,source:{...e.value_badge.source,ref:n}}:e.value_badge;return t===e.controls&&i===e.value_badge?e:{...e,controls:t,value_badge:i}})}(i.markers,c,l)),c&&c!==l&&"derived_marker_state"===g.value_badge?.source?.kind&&g.value_badge.source.ref===`marker:${c}`&&(g.value_badge.source={kind:"derived_marker_state",ref:`marker:${l}`}),i.markers=i.markers.filter(e=>e.id!==l&&e.id!==c&&("virtual"===g.binding||e.binding!==g.binding)),i.markers.push(g);let w=null;const k=c?this._layout[c]:null,x=k?{s:k.s||h?.space||this._space,x:k.x,y:k.y}:c&&h&&this._defPos[c]?this._normPos(h.space,this._defPos[c].x,this._defPos[c].y):null;if(!d&&x&&x.s===u)l===c&&this._layout[l]&&!v||(w={s:x.s,x:x.x,y:x.y},this._layout={...this._layout,[l]:w});else if(d||!this._layout[l]||v){let e=p.vb[0]+p.vb[2]/2,t=p.vb[1]+p.vb[3]/2;const i=r?p.rooms.find(e=>e.id===r):a?p.rooms.find(e=>e.area===a):void 0;i&&([e,t]=this._roomCenter(i)),w=this._normPos(u,e,t),this._layout={...this._layout,[l]:w}}if(await this._saveConfigNow(),w){const e=hl(w);this._layout={...this._layout,[l]:e},this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:l,pos:e}))}const $=new Set(n);c&&c!==l&&$.add(c),$.delete(l);for(const e of $)delete this._layout[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{});y&&b&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:b}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _deleteMarker(){const e=this._markerDialog;if(!e||e.busy||!e.devId)return;const t=e.devId?this._devices.find(t=>t.id===e.devId):null;if(!t)return;const i=e.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===t.bindingKind?"virtual":t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:"";if(!o)return;const a=function(e,t,i,s){const n=new Set([t]),o=e.filter(e=>{const o=e.id===t||!s&&e.binding===i;return o&&n.add(e.id),!o});return{markers:s?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,t.id,o,"virtual"===t.bindingKind);var r,l;s.markers=(r=a.markers,l=a.cleanupIds,r.map(e=>{if(!Array.isArray(e.controls))return e;const t=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&l.has(e.slice(7))));return t.length===e.controls.length?e:{...e,controls:t.length?t:null}}));const c=a.cleanupIds;this._markerDialog={...e,busy:!0};try{await this._saveConfigNow();for(const e of c){delete this._layout[e],delete this._defPos[e],this._dirtyPos.delete(e),this._sentPos.delete(e);const t=this._activityRt.get(e);t&&clearTimeout(t.timer),this._activityRt.delete(e),this._vacRt.delete(e),delete this._vacSrvTrails[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:e}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:e}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===t.id&&this._closeInfoCard(),this._selId===t.id&&(this._selId=null),this._drag?.id===t.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(e){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}_normPos(e,t,i){return{s:e,x:t/cv,y:i/cv}}_openSpaceDialog(e,t){if(this._serverStorage&&this._serverCfg)if("edit"===e){const i=this._serverCfg.spaces.find(e=>e.id===t);if(!i)return;const s=cs(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?rs(i.settings.custom_fill):null,o="none"===s.fill?{...n||as,a:0}:n;this._spaceDialog={mode:e,spaceId:t,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,zeroWallStyle:v_(i),displayTouched:!0,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Pr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:xh(Number(i.cell_cm)>0?Number(i.cell_cm):5,this._imperial),cellCmTouched:!1,busy:!1}}else this._spaceDialog={mode:e,title:"",planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:{...as,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(e){const t=e.target,i=t.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let e=0;e{const t=new Image;t.onload=()=>e(t.naturalWidth&&t.naturalHeight?t.naturalWidth/t.naturalHeight:1.414),t.onerror=()=>e(1.414),t.src=r});URL.revokeObjectURL(r),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:a,aspect:l,name:i.name}}}_useServerPlan(e){const t=this._spaceDialog;t&&(this._spaceDialog={...t,planUrl:e,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(e))}async _readPlanAspect(e){for(let t=0;t<40;t++){const t=this._display(e);if(t){const i=await new Promise(e=>{const i=new Image;i.onload=()=>e(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>e(0),i.src=t}),s=this._spaceDialog;return s&&s.planUrl===e&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(e=>setTimeout(e,150)),this._spaceDialog?.planUrl!==e)return 0}return 0}async _deleteServerPlan(e){if(confirm(this._t("confirm.delete_plan",{name:e})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:e});const t=this._spaceDialog;t?.saved&&(this._spaceDialog={...t,saved:t.saved.filter(t=>t.name!==e)})}catch(e){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(e)}))}}_renderServerPlans(e){if(e.savedBusy)return W`
${this._t("space.loading")}
`;const t=e.saved||[];if(!t.length)return W`
${this._t("space.no_saved")}
`;return W`
${t.map(t=>W`
@@ -5427,7 +5427,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922
`)} -
`}async _saveSpaceDialog(){const e=this._spaceDialog;if(!e||e.busy||!e.title.trim())return;if("file"===e.source&&!e.planFile&&!e.planUrl)return void this._showToast(this._t("toast.plan_required"));const t="create"===e.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...e,busy:!0};try{const n="create"===e.mode?"s"+Date.now().toString(36):e.spaceId;let o=null;if("file"===e.source&&e.planFile){o={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:n,ext:e.planFile.ext,data:e.planFile.b64})).url,aspect:e.planFile.aspect}}let a=e.savedAspect||null;!o&&"file"===e.source&&e.planUrl&&!a&&this._aspectJob&&(a=await this._aspectJob||null);const r=this._serverCfg;let l;if("create"===e.mode)i=n,s=e.title.trim(),l={id:i,title:s,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]},r.spaces.push(l);else{if(l=r.spaces.find(e=>e.id===n),!l)throw new Error("space "+n+" is gone from the config");l.title=e.title.trim()}o?(l.plan_url=o.url,l.plan_aspect=o.aspect):"file"===e.source&&e.planUrl&&e.planUrl!==l.plan_url&&(l.plan_url=e.planUrl,l.plan_aspect=a),"draw"===e.source&&(l.plan_url=null,l.plan_aspect=null,delete l.plan_x,delete l.plan_y,delete l.plan_scale,delete l.plan_scale_x,delete l.plan_scale_y,delete l.plan_angle),l.settings={...l.settings||{},show_borders:e.showBorders,show_names:e.showNames,hide_decor:e.hideDecor||void 0,hide_openings:e.hideOpenings||void 0,room_color:e.roomColor,room_opacity:e.roomOpacity,bg_color:e.bgColor||void 0,bg_mode:e.bgMode||void 0,north_deg:e.northDeg??void 0,sun_rays:e.sunRays??void 0,fill_mode:e.fillMode,custom_fill:e.customFill||void 0,glow_enabled:e.glowEnabled,temp_min:Number.isFinite(e.tempMin)?Math.min(e.tempMin,e.tempMax):20,temp_max:Number.isFinite(e.tempMax)?Math.max(e.tempMin,e.tempMax):25,show_lqi:e.showLqi,card_font_scale:1!==e.cardFontScale?e.cardFontScale:void 0,label_temp:e.labelTemp,label_hum:e.labelHum,label_lqi:e.labelLqi,label_light:e.labelLight},l.zero_wall_style=e.zeroWallStyle,l.cell_cm=Number.isFinite(e.cellCm)&&e.cellCm>0?Math.max(.1,Math.min(Gf,e.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===e.mode&&this._commitSpace(l.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():t||this._importTotal>0?(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(t&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===e.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===e.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(e){"conflict"!==e?.code&&await this._reloadConfigOnly(!0),this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}var i,s}async _deleteSpace(){const e=this._spaceDialog;if(!e||"edit"!==e.mode)return;const t=this._serverCfg;if(!t)return;const i=t.spaces.find(t=>t.id===e.spaceId),s=ug(t,this._layout||{},e.spaceId||""),n=1===t.spaces.length&&t.spaces[0]?.id===e.spaceId;if(!s.count||n){if(confirm(this._t("confirm.delete_space",{title:i.title}))){this._spaceDialog={...e,deleteBlockers:0,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._persistLayout.pending()&&this._persistLayout.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/space/delete",space_id:e.spaceId,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev}),[i,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(i,s),this._cfgRev=t?.config_rev??this._cfgRev,this._layoutRev=t?.layout_rev??this._layoutRev,this._spaceDialog=null,this._space===e.spaceId&&this._commitSpace(this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]);const i=this._serverCfg;if(this._spaceDialog&&i){const t=ug(i,this._layout||{},e.spaceId||""),s=1===i.spaces.length&&i.spaces[0]?.id===e.spaceId;this._spaceDialog={...this._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}else this._spaceDialog={...e,deleteBlockers:s.count}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(e){throw e?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===e?.code&&await this._reloadConfigOnly(),e}}_startImport(){const e=this._importDialog;if(!e)return;const t=e.floors.filter(e=>e.checked).map(e=>e.name);this._importDialog=null,t.length?(this._importQueue=t,this._importTotal=t.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const e=this._importQueue.shift();void 0!==e&&(this._spaceDialog={mode:"create",title:e,planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._showToast(this._t("import.done")))}_renderImportDialog(){const e=this._importDialog,t=e.floors.filter(e=>e.checked).length;return W``}async _saveSpaceDialog(){const e=this._spaceDialog;if(!e||e.busy||!e.title.trim())return;if("file"===e.source&&!e.planFile&&!e.planUrl)return void this._showToast(this._t("toast.plan_required"));const t="create"===e.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...e,busy:!0};try{const n="create"===e.mode?"s"+Date.now().toString(36):e.spaceId;let o=null;if("file"===e.source&&e.planFile){o={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:n,ext:e.planFile.ext,data:e.planFile.b64})).url,aspect:e.planFile.aspect}}let a=e.savedAspect||null;!o&&"file"===e.source&&e.planUrl&&!a&&this._aspectJob&&(a=await this._aspectJob||null);const r=this._serverCfg;let l;if("create"===e.mode)i=n,s=e.title.trim(),l={id:i,title:s,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]},r.spaces.push(l);else{if(l=r.spaces.find(e=>e.id===n),!l)throw new Error("space "+n+" is gone from the config");l.title=e.title.trim()}o?(l.plan_url=o.url,l.plan_aspect=o.aspect):"file"===e.source&&e.planUrl&&e.planUrl!==l.plan_url&&(l.plan_url=e.planUrl,l.plan_aspect=a),"draw"===e.source&&(l.plan_url=null,l.plan_aspect=null,delete l.plan_x,delete l.plan_y,delete l.plan_scale,delete l.plan_scale_x,delete l.plan_scale_y,delete l.plan_angle),l.settings={...l.settings||{},show_borders:e.showBorders,show_names:e.showNames,hide_decor:e.hideDecor||void 0,hide_openings:e.hideOpenings||void 0,room_color:e.roomColor,room_opacity:e.roomOpacity,bg_color:e.bgColor||void 0,bg_mode:e.bgMode||void 0,north_deg:e.northDeg??void 0,sun_rays:e.sunRays??void 0,fill_mode:e.fillMode,custom_fill:e.customFill||void 0,glow_enabled:e.glowEnabled,temp_min:Number.isFinite(e.tempMin)?Math.min(e.tempMin,e.tempMax):20,temp_max:Number.isFinite(e.tempMax)?Math.max(e.tempMin,e.tempMax):25,show_lqi:e.showLqi,card_font_scale:1!==e.cardFontScale?e.cardFontScale:void 0,label_temp:e.labelTemp,label_hum:e.labelHum,label_lqi:e.labelLqi,label_light:e.labelLight},l.zero_wall_style=e.zeroWallStyle,l.cell_cm=Number.isFinite(e.cellCm)&&e.cellCm>0?Math.max(.1,Math.min(Vf,e.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===e.mode&&this._commitSpace(l.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():t||this._importTotal>0?(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(t&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===e.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===e.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(e){"conflict"!==e?.code&&await this._reloadConfigOnly(!0),this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}var i,s}async _deleteSpace(){const e=this._spaceDialog;if(!e||"edit"!==e.mode)return;const t=this._serverCfg;if(!t)return;const i=t.spaces.find(t=>t.id===e.spaceId),s=_g(t,this._layout||{},e.spaceId||""),n=1===t.spaces.length&&t.spaces[0]?.id===e.spaceId;if(!s.count||n){if(confirm(this._t("confirm.delete_space",{title:i.title}))){this._spaceDialog={...e,deleteBlockers:0,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._persistLayout.pending()&&this._persistLayout.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/space/delete",space_id:e.spaceId,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev}),[i,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(i,s),this._cfgRev=t?.config_rev??this._cfgRev,this._layoutRev=t?.layout_rev??this._layoutRev,this._spaceDialog=null,this._space===e.spaceId&&this._commitSpace(this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]);const i=this._serverCfg;if(this._spaceDialog&&i){const t=_g(i,this._layout||{},e.spaceId||""),s=1===i.spaces.length&&i.spaces[0]?.id===e.spaceId;this._spaceDialog={...this._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}else this._spaceDialog={...e,deleteBlockers:s.count}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(e){throw e?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===e?.code&&await this._reloadConfigOnly(),e}}_startImport(){const e=this._importDialog;if(!e)return;const t=e.floors.filter(e=>e.checked).map(e=>e.name);this._importDialog=null,t.length?(this._importQueue=t,this._importTotal=t.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const e=this._importQueue.shift();void 0!==e&&(this._spaceDialog={mode:"create",title:e,planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._showToast(this._t("import.done")))}_renderImportDialog(){const e=this._importDialog,t=e.floors.filter(e=>e.checked).length;return W`this._importDialog=null}>
${this._t("import.hint")}
@@ -5481,7 +5481,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${this._t("gs.north_letter")} ${null===e?"—":e+"°"} - `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,s=t?t.bgColor||"":hs(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||s}_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){const i=new Map((t?.spaces||[]).map(e=>[String(e?.id||""),e]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:qf,checkedAt:(new Date).toISOString(),preflightFingerprint:e.fingerprint,failures:e.failures.map(e=>({spaceId:e.spaceId,displayName:e.displayName,reason:e.reason,detail:e.detail??null,spaceGeometryFingerprint:i.has(e.spaceId)?mg(i.get(e.spaceId)):null}))}}_reportPreflightFailure(e,t){e.ok||e.fingerprint===this._reportedPreflightFingerprint||(this._reportedPreflightFingerprint=e.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(e,t)))}_preflightVersionsDiffer(){const e=this._haIntegrationVersion;return"string"==typeof e&&e.length>0&&e!==qf}async _copyPreflightDiagnostics(){const e=this._alignDialog?.preflight;if(!e||e.ok)return;const t=JSON.stringify(this._preflightDiagnostics(e,this._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(t),this._preflightClipboardFallback=null,this._showToast(this._t("gs.preflight_copied"))}catch{this._preflightClipboardFallback=t}}_checkOptimizeGeometry(e){return wg(e,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)})})}_checkSpacePhysicalGeometry(e,t,i){return function(e,t,i={}){const s=(Array.isArray(e?.spaces)?e.spaces:[]).find(e=>String(e?.id||"")===String(t||"")),n=mg(s);if(!s)return{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:n,ok:!1};const o=wg({...e,spaces:[s]},{...i,fingerprint:()=>n}).spaces[0]||{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:n,ok:"failed"!==o.status}}(e,t,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)}),captureWallGeometry:i?(e,t)=>i(t):void 0})}_optimizeReferenceContext(e){const t=this._haRegistry,i=this._fullRegistryHass,s={},n=(...e)=>{for(const t of e){const e=String(t||"").trim();if(e)return e}return""};for(const[e,i]of Object.entries(t.devices||{}))s[e]=n(i?.name_by_user,i?.name,i?.model);for(const[e,i]of Object.entries(t.entities||{}))s[`lg_${e}`]=n(this.hass?.states?.[e]?.attributes?.friendly_name,i?.name,i?.original_name);for(const e of this._devices)s[e.id]=n(e.name,s[e.id]);for(const e of this._serverCfg?.markers||[]){const t=String(e.binding||"").indexOf(":"),o=t>0?e.binding.slice(0,t):"",a=t>0?e.binding.slice(t+1):"",r="device"===o?i?.devices?.[a]:"entity"===o?i?.entities?.[a]:null;s[e.id]=n(e.name,"device"===o?r?.name_by_user:null,r?.name,"entity"===o?this.hass?.states?.[a]?.attributes?.friendly_name:null,s[e.id])}return{effectiveAreaByMarker:Object.fromEntries(this._devices.filter(e=>!e.virtual&&!!e.area).map(e=>[e.id,e.area])),ownerRoster:{authoritative:t.authoritative,deviceIds:Object.keys(t.devices||{}),entityIds:[...new Set([...Object.keys(t.entities||{}),...Object.keys(this.hass?.states||{})])],names:s},removeLiveMissingPositions:e}}_previewAlignDialog(e){if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[];let i;try{i=pg(this._serverCfg,this._layout||{},this._optimizeReferenceContext(e))}catch(e){return void this._showWallModelMigrationBlocked(e)}const s=i.changed?this._checkOptimizeGeometry(i.config):null;s&&this._reportPreflightFailure(s,i.config);const n=Math.ceil(10*i.report.maxShiftCm)/10,o=t.find(e=>null!=e?.id&&String(e.id)===i.report.maxSpace),a=t.length>1&&o?String(o.title||o.id):"";this._preflightClipboardFallback=null,this._alignDialog={report:i.report,config:i.config,layout:i.layout,cm:n,where:a,preflight:s,changed:i.changed,busy:!1,removeLiveMissingPositions:e}}async _runAlignToGrid(){let e=this._alignDialog;if(!e||e.busy||!this._serverCfg||!e.changed||!e.preflight?.ok)return;const t=Md(e.config);if(e.preflight.fingerprint!==t){const t=this._checkOptimizeGeometry(e.config);if(this._reportPreflightFailure(t,e.config),e={...e,preflight:t},this._alignDialog=e,!t.ok)return}this._clearGeometryGesture(),this._alignDialog={...e,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/plan/optimize",config:e.config,layout:e.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=e.config,this._layout=e.layout,this._geometryHistory.clear(),this._cfgRev=t?.config_rev??this._cfgRev+1,this._layoutRev=t?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!t?.can_undo,this._undoKind=t?.can_undo?"optimize":null,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this._alignDialog=null,this._preflightClipboardFallback=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(e.report.moved),m:String(e.report.migrated+e.report.canonicalized+e.report.coordsCanonicalized+e.report.latticeCoordinatesCanonicalized+e.report.wallsMerged+e.report.spansMerged+e.report.partitionsMerged+e.report.partitionsReconciled+e.report.openingsRehosted+e.report.wallsStraightened),r:String(e.report.spaceRefsRemapped+e.report.roomRefsRemapped+e.report.positionsRemapped+e.report.markersDetached+e.report.orphanRoomLabelsRemoved+e.report.orphanDevicePositionsRemoved+e.report.orphanGroupPositionsRemoved)}))}catch(e){if(this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"wall_model_client_outdated"===e?.code)return void this._showToast(this._t("toast.wall_model_client_outdated"));"conflict"===e?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _undoPlanOptimization(){if(!this._canOptimizeUndo||this._optimizeUndoBusy)return;const e=this._undoKind;this._clearGeometryGesture(),this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,i]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=i?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=i?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._undoKind=null,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("import"===e?"backup.import_undone":"gs.optimize_undone"))}catch(e){this._canOptimizeUndo=!1,this._undoKind=null,this._showToast(this._t("toast.error",{err:this._errText(e)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}async _runBackupExport(){const e=this._backupExportDialog;if(e&&!e.busy){this._backupExportDialog={...e,busy:!0,error:""};try{const t=await this.hass.callWS({type:"houseplan/export/create",kind:e.kind,space_id:"space"===e.kind?this._space:void 0,..."space"===e.kind&&e.planOnly?{plan_only:!0}:{},card_version:qf}),i=new Blob([JSON.stringify(t.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(i),n=document.createElement("a");n.href=s,n.download=t.filename||`houseplan-${e.kind}.json`,n.style.display="none",document.body.append(n),n.click(),n.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0),this._backupExportDialog=null,this._showToast(this._t("backup.export_done"))}catch(e){this._backupExportDialog&&(this._backupExportDialog={...this._backupExportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _pickBackupImport(e){const t=e.target,i=t.files?.[0];if(t.value="",i){this._settingsDialog=null,this._backupImportDialog={filename:i.name,size:i.size,token:"",preview:null,expectedConfigRev:this._cfgRev,expectedLayoutRev:this._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain,this._persistLayout.pending()&&this._persistLayout.flush();const e="/api/houseplan/import/preview?duplicate_policy=skip",t={method:"POST",body:i,headers:{"content-type":"application/json"}},s=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth(e,t):await fetch(e,{...t,headers:{...t.headers,...this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}}),n=await s.json().catch(()=>({}));if(!s.ok||n.error)throw Object.assign(new Error(n.message||n.error||`HTTP ${s.status}`),{code:n.error});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,token:n.token,preview:n.preview,expectedConfigRev:n.expected_config_rev,expectedLayoutRev:n.expected_layout_rev,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _setBackupDuplicatePolicy(e){const t=this._backupImportDialog;if(t&&!t.busy&&t.token&&e!==t.duplicatePolicy){this._backupImportDialog={...t,duplicatePolicy:e,busy:!0,error:""};try{const i=await this.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:e});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,preview:{...t.preview,...i.preview},expectedConfigRev:i.expected_config_rev,expectedLayoutRev:i.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,duplicatePolicy:t.duplicatePolicy,preview:t.preview,confirmMissing:t.confirmMissing,busy:!1,error:this._backupErrorText(e)})}}}async _applyBackupImport(){const e=this._backupImportDialog;if(!e||e.busy||!e.token||!e.preview)return;if(e.preview.confirmation_required&&!e.confirmMissing)return;this._backupImportDialog={...e,busy:!0,error:""};const t=this._space;try{const i=await this.hass.callWS({type:"houseplan/import/apply",token:e.token,expected_config_rev:e.expectedConfigRev,expected_layout_rev:e.expectedLayoutRev,duplicate_policy:e.duplicatePolicy,confirm_missing_content:e.confirmMissing}),[s,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(s,n),this._geometryHistory.clear(),this._dirtyPos.clear(),this._sentPos.clear(),this._defPos={},this._cfgEpoch++,this._modelCache=null,this._frame=null,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotConfigEpoch=-1,this._regSignature="",this._signer.invalidate(this.hass),this._resign(),this._maybeRebuildDevices();const o=this._serverCfg?.spaces||[],a="space"===i.kind&&i.space_id?i.space_id:o.some(e=>e.id===t)?t:o[0]?.id||this._space;this._hasFixedFloor?this._adoptInitialSpace(this._model,!0):this._commitSpace(a),this._backupImportDialog=null,this._cacheSnapshot(),this.requestUpdate();const r="space"===i.kind?e.preview.counts:i.counts;this._showToast(this._t("space"===i.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(i.repaired_target_refs||0)}))}catch(e){if("conflict"===e?.code&&this._backupImportDialog?.token)try{const e=this._backupImportDialog,t=await this.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:e.duplicatePolicy});return void(this._backupImportDialog={...e,preview:{...e.preview,...t.preview},expectedConfigRev:t.expected_config_rev,expectedLayoutRev:t.expected_layout_rev,confirmMissing:!1,busy:!1,error:this._t("backup.revalidated")})}catch(t){e=t}this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}_renderBackupExportDialog(){const e=this._backupExportDialog,t=(this._serverCfg?.spaces||[]).find(e=>e.id===this._space);return W``}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,s=t?t.bgColor||"":hs(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||s}_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){const i=new Map((t?.spaces||[]).map(e=>[String(e?.id||""),e]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:Wf,checkedAt:(new Date).toISOString(),preflightFingerprint:e.fingerprint,failures:e.failures.map(e=>({spaceId:e.spaceId,displayName:e.displayName,reason:e.reason,detail:e.detail??null,spaceGeometryFingerprint:i.has(e.spaceId)?gg(i.get(e.spaceId)):null}))}}_reportPreflightFailure(e,t){e.ok||e.fingerprint===this._reportedPreflightFingerprint||(this._reportedPreflightFingerprint=e.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(e,t)))}_preflightVersionsDiffer(){const e=this._haIntegrationVersion;return"string"==typeof e&&e.length>0&&e!==Wf}async _copyPreflightDiagnostics(){const e=this._alignDialog?.preflight;if(!e||e.ok)return;const t=JSON.stringify(this._preflightDiagnostics(e,this._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(t),this._preflightClipboardFallback=null,this._showToast(this._t("gs.preflight_copied"))}catch{this._preflightClipboardFallback=t}}_checkOptimizeGeometry(e){return kg(e,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)})})}_checkSpacePhysicalGeometry(e,t,i){return function(e,t,i={}){const s=(Array.isArray(e?.spaces)?e.spaces:[]).find(e=>String(e?.id||"")===String(t||"")),n=gg(s);if(!s)return{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:n,ok:!1};const o=kg({...e,spaces:[s]},{...i,fingerprint:()=>n}).spaces[0]||{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:n,ok:"failed"!==o.status}}(e,t,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)}),captureWallGeometry:i?(e,t)=>i(t):void 0})}_optimizeReferenceContext(e){const t=this._haRegistry,i=this._fullRegistryHass,s={},n=(...e)=>{for(const t of e){const e=String(t||"").trim();if(e)return e}return""};for(const[e,i]of Object.entries(t.devices||{}))s[e]=n(i?.name_by_user,i?.name,i?.model);for(const[e,i]of Object.entries(t.entities||{}))s[`lg_${e}`]=n(this.hass?.states?.[e]?.attributes?.friendly_name,i?.name,i?.original_name);for(const e of this._devices)s[e.id]=n(e.name,s[e.id]);for(const e of this._serverCfg?.markers||[]){const t=String(e.binding||"").indexOf(":"),o=t>0?e.binding.slice(0,t):"",a=t>0?e.binding.slice(t+1):"",r="device"===o?i?.devices?.[a]:"entity"===o?i?.entities?.[a]:null;s[e.id]=n(e.name,"device"===o?r?.name_by_user:null,r?.name,"entity"===o?this.hass?.states?.[a]?.attributes?.friendly_name:null,s[e.id])}return{effectiveAreaByMarker:Object.fromEntries(this._devices.filter(e=>!e.virtual&&!!e.area).map(e=>[e.id,e.area])),ownerRoster:{authoritative:t.authoritative,deviceIds:Object.keys(t.devices||{}),entityIds:[...new Set([...Object.keys(t.entities||{}),...Object.keys(this.hass?.states||{})])],names:s},removeLiveMissingPositions:e}}_previewAlignDialog(e){if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[];let i;try{i=ug(this._serverCfg,this._layout||{},this._optimizeReferenceContext(e))}catch(e){return void this._showWallModelMigrationBlocked(e)}const s=i.changed?this._checkOptimizeGeometry(i.config):null;s&&this._reportPreflightFailure(s,i.config);const n=Math.ceil(10*i.report.maxShiftCm)/10,o=t.find(e=>null!=e?.id&&String(e.id)===i.report.maxSpace),a=t.length>1&&o?String(o.title||o.id):"";this._preflightClipboardFallback=null,this._alignDialog={report:i.report,config:i.config,layout:i.layout,cm:n,where:a,preflight:s,changed:i.changed,busy:!1,removeLiveMissingPositions:e}}async _runAlignToGrid(){let e=this._alignDialog;if(!e||e.busy||!this._serverCfg||!e.changed||!e.preflight?.ok)return;const t=Md(e.config);if(e.preflight.fingerprint!==t){const t=this._checkOptimizeGeometry(e.config);if(this._reportPreflightFailure(t,e.config),e={...e,preflight:t},this._alignDialog=e,!t.ok)return}this._clearGeometryGesture(),this._alignDialog={...e,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/plan/optimize",config:e.config,layout:e.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=e.config,this._layout=e.layout,this._geometryHistory.clear(),this._cfgRev=t?.config_rev??this._cfgRev+1,this._layoutRev=t?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!t?.can_undo,this._undoKind=t?.can_undo?"optimize":null,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this._alignDialog=null,this._preflightClipboardFallback=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(e.report.moved),m:String(e.report.migrated+e.report.canonicalized+e.report.coordsCanonicalized+e.report.latticeCoordinatesCanonicalized+e.report.wallsMerged+e.report.spansMerged+e.report.partitionsMerged+e.report.partitionsReconciled+e.report.openingsRehosted+e.report.wallsStraightened),r:String(e.report.spaceRefsRemapped+e.report.roomRefsRemapped+e.report.positionsRemapped+e.report.markersDetached+e.report.orphanRoomLabelsRemoved+e.report.orphanDevicePositionsRemoved+e.report.orphanGroupPositionsRemoved)}))}catch(e){if(this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"wall_model_client_outdated"===e?.code)return void this._showToast(this._t("toast.wall_model_client_outdated"));"conflict"===e?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _undoPlanOptimization(){if(!this._canOptimizeUndo||this._optimizeUndoBusy)return;const e=this._undoKind;this._clearGeometryGesture(),this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,i]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=i?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=i?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._undoKind=null,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("import"===e?"backup.import_undone":"gs.optimize_undone"))}catch(e){this._canOptimizeUndo=!1,this._undoKind=null,this._showToast(this._t("toast.error",{err:this._errText(e)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}async _runBackupExport(){const e=this._backupExportDialog;if(e&&!e.busy){this._backupExportDialog={...e,busy:!0,error:""};try{const t=await this.hass.callWS({type:"houseplan/export/create",kind:e.kind,space_id:"space"===e.kind?this._space:void 0,..."space"===e.kind&&e.planOnly?{plan_only:!0}:{},card_version:Wf}),i=new Blob([JSON.stringify(t.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(i),n=document.createElement("a");n.href=s,n.download=t.filename||`houseplan-${e.kind}.json`,n.style.display="none",document.body.append(n),n.click(),n.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0),this._backupExportDialog=null,this._showToast(this._t("backup.export_done"))}catch(e){this._backupExportDialog&&(this._backupExportDialog={...this._backupExportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _pickBackupImport(e){const t=e.target,i=t.files?.[0];if(t.value="",i){this._settingsDialog=null,this._backupImportDialog={filename:i.name,size:i.size,token:"",preview:null,expectedConfigRev:this._cfgRev,expectedLayoutRev:this._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain,this._persistLayout.pending()&&this._persistLayout.flush();const e="/api/houseplan/import/preview?duplicate_policy=skip",t={method:"POST",body:i,headers:{"content-type":"application/json"}},s=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth(e,t):await fetch(e,{...t,headers:{...t.headers,...this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}}),n=await s.json().catch(()=>({}));if(!s.ok||n.error)throw Object.assign(new Error(n.message||n.error||`HTTP ${s.status}`),{code:n.error});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,token:n.token,preview:n.preview,expectedConfigRev:n.expected_config_rev,expectedLayoutRev:n.expected_layout_rev,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _setBackupDuplicatePolicy(e){const t=this._backupImportDialog;if(t&&!t.busy&&t.token&&e!==t.duplicatePolicy){this._backupImportDialog={...t,duplicatePolicy:e,busy:!0,error:""};try{const i=await this.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:e});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,preview:{...t.preview,...i.preview},expectedConfigRev:i.expected_config_rev,expectedLayoutRev:i.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,duplicatePolicy:t.duplicatePolicy,preview:t.preview,confirmMissing:t.confirmMissing,busy:!1,error:this._backupErrorText(e)})}}}async _applyBackupImport(){const e=this._backupImportDialog;if(!e||e.busy||!e.token||!e.preview)return;if(e.preview.confirmation_required&&!e.confirmMissing)return;this._backupImportDialog={...e,busy:!0,error:""};const t=this._space;try{const i=await this.hass.callWS({type:"houseplan/import/apply",token:e.token,expected_config_rev:e.expectedConfigRev,expected_layout_rev:e.expectedLayoutRev,duplicate_policy:e.duplicatePolicy,confirm_missing_content:e.confirmMissing}),[s,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(s,n),this._geometryHistory.clear(),this._dirtyPos.clear(),this._sentPos.clear(),this._defPos={},this._cfgEpoch++,this._modelCache=null,this._frame=null,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotConfigEpoch=-1,this._regSignature="",this._signer.invalidate(this.hass),this._resign(),this._maybeRebuildDevices();const o=this._serverCfg?.spaces||[],a="space"===i.kind&&i.space_id?i.space_id:o.some(e=>e.id===t)?t:o[0]?.id||this._space;this._hasFixedFloor?this._adoptInitialSpace(this._model,!0):this._commitSpace(a),this._backupImportDialog=null,this._cacheSnapshot(),this.requestUpdate();const r="space"===i.kind?e.preview.counts:i.counts;this._showToast(this._t("space"===i.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(i.repaired_target_refs||0)}))}catch(e){if("conflict"===e?.code&&this._backupImportDialog?.token)try{const e=this._backupImportDialog,t=await this.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:e.duplicatePolicy});return void(this._backupImportDialog={...e,preview:{...e.preview,...t.preview},expectedConfigRev:t.expected_config_rev,expectedLayoutRev:t.expected_layout_rev,confirmMissing:!1,busy:!1,error:this._t("backup.revalidated")})}catch(t){e=t}this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}_renderBackupExportDialog(){const e=this._backupExportDialog,t=(this._serverCfg?.spaces||[]).find(e=>e.id===this._space);return W`this._backupExportDialog=null}>
${this._t("backup.export_hint")}
@@ -5587,12 +5587,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 .pickerLabels=${this._colorPickerLabels} .color=${i.c} .opacity=${i.a} .showOpacity=${!0} @hp-color-opacity-change=${t=>{this._setFillColor(e,{c:t.detail.color,a:t.detail.opacity})}}> -
`}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){let i=this._glowRenderedSources.get(e);if(t){const t=this._glowFadeTimers.get(e);if(null!=t&&(clearTimeout(t),this._glowFadeTimers.delete(e)),null==i){this._suspendGlowFeatherForTransition(),i=++this._glowSourceSeq,this._glowRenderedSources.set(e,i),this._glowEnteringSources.add(e);const t=requestAnimationFrame(()=>{this._glowEnterRafs.get(e)===t&&(this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),this.isConnected&&this.requestUpdate())});this._glowEnterRafs.set(e,t)}return{domId:i,entering:this._glowEnteringSources.has(e),leaving:!1}}if(null==i)return null;const s=this._glowEnterRafs.get(e);if(null!=s&&cancelAnimationFrame(s),this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),!this._glowFadeTimers.has(e)){this._suspendGlowFeatherForTransition();const t=window.setTimeout(()=>{this._glowFadeTimers.get(e)===t&&(this._glowFadeTimers.delete(e),this._glowRenderedSources.delete(e),this._glowLastAppearance.delete(e),this.isConnected&&this.requestUpdate())},534);this._glowFadeTimers.set(e,t)}return{domId:i,entering:!1,leaving:!0}}_suspendGlowFeatherForTransition(){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;this._glowFeatherSuspendUntil=Math.max(this._glowFeatherSuspendUntil,Date.now()+500),clearTimeout(this._glowFeatherResumeTimer);const e=Math.max(0,this._glowFeatherSuspendUntil-Date.now())+17,t=()=>{this._glowFeatherResumeTimer=0,Date.now()=128){const e=this._glowGeometryWarnings.values().next().value;e&&this._glowGeometryWarnings.delete(e)}this._glowGeometryWarnings.add(n),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${e}, room ${i}, phase ${s}`)}}_lightBarriers(e,t){const i=Md([this._curSpaceCfg,this._cellCm,this._gridPitch]),s=`${e.id}|${i}`,n=Yf(this._lightBarrierPool,s);if(n.hit)return this._lightBarrierCache=n.value,n.value.value;const o=this._zeroWalls(),a=this._openCuts(),r=[...o.transmissive],l=Math.max(this._cmToUnits(10),.5*this._gridPitch),c=e=>t.some(t=>this._pointInRoom(e,t.r)),h=this._openingsR.filter(e=>{if("door"!==(t=String(e.type))&&"gate"!==t&&"passage"!==t)return!1;var t;const i=e.angle*Math.PI/180,s=-Math.sin(i),n=Math.cos(i);return c([e.rx+s*l,e.ry+n*l])&&c([e.rx-s*l,e.ry-n*l])}),d=this._roomWallOpeningInputs(h,e);for(const e of d){const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;r.push([e.x-i,e.y-s,e.x+i,e.y+s])}const p=new Set(h.filter(e=>e.partitionHost).map(e=>e.id)),u=[e.id,this._cfgEpoch,this._cellCm,this._gridPitch,[...p].sort().join(",")].join("|");this._lightPhysicalBodiesCache?.key!==u&&(this._lightPhysicalBodiesCache={key:u,all:rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e,e=>p.has(e.id))).all});const _=this._lightPhysicalBodiesCache.all,m=this._spaceWalls,g=.02*this._gridPitch,f=[],v=this._wallUnionGeometry(),y=v?.sourceFingerprint,b=v&&y===i?function(e,t=[],i=[],s={}){if("failed-core"===e.status||"not-applicable"===e.status)return null;const n=e.roomComponents?.length?e.roomComponents:$c(e.roomGeom)?[{id:"room-primary",geom:e.roomGeom}]:[];let o=n[0]?.geom||null;const a=n.slice(1).map(e=>({...e}));try{for(const i of t){if(!(i.length>0&&e.openingIndex))continue;const t=jc(e.openingIndex,i,!0);if(!t.negative&&!t.positive)continue;const s=i.angle*Math.PI/180,n=Math.cos(s),r=Math.sin(s),l=-r,c=n,h=i.length/2,d=e.openingPadUnits??1.25*Math.max(e.depthUnits,1),p=xc([[i.x-n*h-l*d,i.y-r*h-c*d],[i.x+n*h-l*d,i.y+r*h-c*d],[i.x+n*h+l*d,i.y+r*h+c*d],[i.x-n*h+l*d,i.y-r*h+c*d]]);o&&(o=oi(o,p));for(const e of a)e.geom=oi(e.geom,p)}const n=[...a];let r=a.length;const l=s.mergeExtra||((e,t)=>e?si(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(Ti(t))<=1e-9){r++;continue}const s=[xc(t)];try{const t=l(o,s,e);if(!$c(t))throw new Error("invalid extra union");o=t}catch{r++,$c(s)&&n.push({id:`policy-extra-${e}`,geom:s})}}n.sort((e,t)=>Fc(e.geom).localeCompare(Fc(t.geom)));const c=o||[];return{status:r?"degraded-extra":"ok",geom:c,components:[...$c(c)?[{id:"primary",geom:c}]:[],...n.map((e,t)=>({...e,id:`isolated-${t}`}))]}}catch{return null}}(v,d,_):null,w=b||(m.length||_.length?Ec(e.rooms,m,a,d,this._wallKeyPitch,this._cellCm,this._gridPitch,lv,_):null);if(!w||"ok"!==w.status&&"degraded-extra"!==w.status)for(const e of _)f.push(...Lu(e));else for(const e of w.components)for(const t of _h(e.geom))f.push(...Lu(t));for(const{poly:e}of t)for(const t of r.length?Ps(e,r,g):Lu(e))f.push(t);for(const e of o.barriers)f.push(e);const k={occluders:Hu(f),floor:t.map(e=>e.poly),fingerprint:i,masonryGeometry:!w||"ok"!==w.status&&"degraded-extra"!==w.status?[]:w.components.flatMap(e=>e.geom),opaqueBodies:_},x={key:s,value:k};return Jf(this._lightBarrierPool,s,x,8),this._lightBarrierCache=x,k}_renderGlowLayer(e,t){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,n=e.rooms.map(e=>({r:e,poly:hi(e)})).filter(e=>!!e.poly),o=n.filter(({r:e})=>Fs(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const{occluders:a,floor:r,fingerprint:l,masonryGeometry:c,opaqueBodies:h}=this._lightBarriers(e,n),d=ho(this._renderPlanHass,this._renderDevices,null,this._virtualLights).filter(t=>t.device.space===e.id),p=new Map;for(const e of d){if(!e.device.id)continue;const t=p.get(e.device.id)||[];t.push(e),p.set(e.device.id,t)}const u=[],_=new Set;for(const t of this._renderDevices){if(t.space!==e.id)continue;const o=po(p.get(t.id)||[]);if(!o)continue;const d=`${e.id}|${t.id}`;_.add(d);const m=$s(o.passive?{state:o.on?"on":"off",attributes:{}}:this._renderPlanHass.states[o.eid],t.marker?.glow_color,i.glow_light.c),g=Number(t.marker?.glow_radius_cm),f=Number.isFinite(g)&&g>0?g/this._cellCm*this._gridPitch:s,v=this._pos(t);if(fh([v.x,v.y],c,h)){this._forgetGlowSource(d);continue}const y=this._glowTransition(d,!!m);if(!y)continue;m&&this._glowLastAppearance.set(d,{c:m.c,alpha:Ss(m.bri,i.glow_light.a)});const b=this._glowLastAppearance.get(d);if(!b)continue;let w=null;const k=`${e.id}|${l}|${v.x.toFixed(4)},${v.y.toFixed(4)}|${f.toFixed(4)}`,x=Yf(this._glowClipCache,k);if(x.hit)w=x.value;else{const t=Wu([v.x,v.y],f,a,96);w={lit:t.length>=3?dh([t],r,{onBoundsFailure:({boundIndex:t,phase:i})=>{const s=n[t]?.r;this._warnGlowGeometryFallback(e.id,l,s?.id||`#${t}`,i)}}):[]},Jf(this._glowClipCache,k,w,256)}u.push({key:d,sourceEid:o.eid,domId:y.domId,entering:y.entering,leaving:y.leaving,pos:v,c:b.c,alpha:b.alpha,geometry:w,r:f})}const m=`${e.id}|`;for(const e of this._glowRenderedSources.keys())e.startsWith(m)&&!_.has(e)&&this._forgetGlowSource(e);if(!u.length)return j``;const g=this._spaceWalls,f=o.length===n.length?[]:this._openCuts(),v=this._wallUnionGeometry()?.roomGeom,y=o.length===n.length?null:o.map(({r:t,poly:i})=>{const s=g.length&&t.id&&this._innerRoomContour(e,t.id,f,v)||i,o=this._cleanFloor(t,s,e).path,a=Pi(s,n.filter(e=>e.r!==t).map(e=>e.poly)),r=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||r(s),...a.map(r)].join(" ")}),b=this._viewOr(this._baseVb()),w=this._stageEl?.clientWidth&&b.w?this._stageEl.clientWidth/b.w:1,k=1/(w>0?w:1),x=!this._pinchStart&&!this._panStart&&Date.now()>=this._glowFeatherSuspendUntil;(null==this._glowFeatherUnits||x)&&(this._glowFeatherUnits=k);const $=this._glowFeatherUnits??k,S=4*$,M=u.reduce((e,t)=>({x:Math.min(e.x,t.pos.x-t.r-S),y:Math.min(e.y,t.pos.y-t.r-S),maxX:Math.max(e.maxX,t.pos.x+t.r+S),maxY:Math.max(e.maxY,t.pos.y+t.r+S),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});return M.w=M.maxX-M.x,M.h=M.maxY-M.y,j` +
`}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){let i=this._glowRenderedSources.get(e);if(t){const t=this._glowFadeTimers.get(e);if(null!=t&&(clearTimeout(t),this._glowFadeTimers.delete(e)),null==i){this._suspendGlowFeatherForTransition(),i=++this._glowSourceSeq,this._glowRenderedSources.set(e,i),this._glowEnteringSources.add(e);const t=requestAnimationFrame(()=>{this._glowEnterRafs.get(e)===t&&(this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),this.isConnected&&this.requestUpdate())});this._glowEnterRafs.set(e,t)}return{domId:i,entering:this._glowEnteringSources.has(e),leaving:!1}}if(null==i)return null;const s=this._glowEnterRafs.get(e);if(null!=s&&cancelAnimationFrame(s),this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),!this._glowFadeTimers.has(e)){this._suspendGlowFeatherForTransition();const t=window.setTimeout(()=>{this._glowFadeTimers.get(e)===t&&(this._glowFadeTimers.delete(e),this._glowRenderedSources.delete(e),this._glowLastAppearance.delete(e),this.isConnected&&this.requestUpdate())},534);this._glowFadeTimers.set(e,t)}return{domId:i,entering:!1,leaving:!0}}_suspendGlowFeatherForTransition(){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;this._glowFeatherSuspendUntil=Math.max(this._glowFeatherSuspendUntil,Date.now()+500),clearTimeout(this._glowFeatherResumeTimer);const e=Math.max(0,this._glowFeatherSuspendUntil-Date.now())+17,t=()=>{this._glowFeatherResumeTimer=0,Date.now()=128){const e=this._glowGeometryWarnings.values().next().value;e&&this._glowGeometryWarnings.delete(e)}this._glowGeometryWarnings.add(n),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${e}, room ${i}, phase ${s}`)}}_lightBarriers(e,t){const i=Md([this._curSpaceCfg,this._cellCm,this._gridPitch]),s=`${e.id}|${i}`,n=Jf(this._lightBarrierPool,s);if(n.hit)return this._lightBarrierCache=n.value,n.value.value;const o=this._zeroWalls(),a=this._openCuts(),r=[...o.transmissive],l=Math.max(this._cmToUnits(10),.5*this._gridPitch),c=e=>t.some(t=>this._pointInRoom(e,t.r)),h=this._openingsR.filter(e=>{if("door"!==(t=String(e.type))&&"gate"!==t&&"passage"!==t)return!1;var t;const i=e.angle*Math.PI/180,s=-Math.sin(i),n=Math.cos(i);return c([e.rx+s*l,e.ry+n*l])&&c([e.rx-s*l,e.ry-n*l])}),d=this._roomWallOpeningInputs(h,e);for(const e of d){const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;r.push([e.x-i,e.y-s,e.x+i,e.y+s])}const p=new Set(h.filter(e=>e.partitionHost).map(e=>e.id)),u=[e.id,this._cfgEpoch,this._cellCm,this._gridPitch,[...p].sort().join(",")].join("|");this._lightPhysicalBodiesCache?.key!==u&&(this._lightPhysicalBodiesCache={key:u,all:rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e,e=>p.has(e.id))).all});const _=this._lightPhysicalBodiesCache.all,m=this._spaceWalls,g=.02*this._gridPitch,f=[],v=this._wallUnionGeometry(),y=v?.sourceFingerprint,b=v&&y===i?function(e,t=[],i=[],s={}){if("failed-core"===e.status||"not-applicable"===e.status)return null;const n=e.roomComponents?.length?e.roomComponents:$c(e.roomGeom)?[{id:"room-primary",geom:e.roomGeom}]:[];let o=n[0]?.geom||null;const a=n.slice(1).map(e=>({...e}));try{for(const i of t){if(!(i.length>0&&e.openingIndex))continue;const t=jc(e.openingIndex,i,!0);if(!t.negative&&!t.positive)continue;const s=i.angle*Math.PI/180,n=Math.cos(s),r=Math.sin(s),l=-r,c=n,h=i.length/2,d=e.openingPadUnits??1.25*Math.max(e.depthUnits,1),p=xc([[i.x-n*h-l*d,i.y-r*h-c*d],[i.x+n*h-l*d,i.y+r*h-c*d],[i.x+n*h+l*d,i.y+r*h+c*d],[i.x-n*h+l*d,i.y-r*h+c*d]]);o&&(o=oi(o,p));for(const e of a)e.geom=oi(e.geom,p)}const n=[...a];let r=a.length;const l=s.mergeExtra||((e,t)=>e?si(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(Ti(t))<=1e-9){r++;continue}const s=[xc(t)];try{const t=l(o,s,e);if(!$c(t))throw new Error("invalid extra union");o=t}catch{r++,$c(s)&&n.push({id:`policy-extra-${e}`,geom:s})}}n.sort((e,t)=>Fc(e.geom).localeCompare(Fc(t.geom)));const c=o||[];return{status:r?"degraded-extra":"ok",geom:c,components:[...$c(c)?[{id:"primary",geom:c}]:[],...n.map((e,t)=>({...e,id:`isolated-${t}`}))]}}catch{return null}}(v,d,_):null,w=b||(m.length||_.length?Ec(e.rooms,m,a,d,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,_):null);if(!w||"ok"!==w.status&&"degraded-extra"!==w.status)for(const e of _)f.push(...Hu(e));else for(const e of w.components)for(const t of _h(e.geom))f.push(...Hu(t));for(const{poly:e}of t)for(const t of r.length?Ps(e,r,g):Hu(e))f.push(t);for(const e of o.barriers)f.push(e);const k={occluders:Lu(f),floor:t.map(e=>e.poly),fingerprint:i,masonryGeometry:!w||"ok"!==w.status&&"degraded-extra"!==w.status?[]:w.components.flatMap(e=>e.geom),opaqueBodies:_},x={key:s,value:k};return Xf(this._lightBarrierPool,s,x,8),this._lightBarrierCache=x,k}_renderGlowLayer(e,t){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,n=e.rooms.map(e=>({r:e,poly:hi(e)})).filter(e=>!!e.poly),o=n.filter(({r:e})=>Fs(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const{occluders:a,floor:r,fingerprint:l,masonryGeometry:c,opaqueBodies:h}=this._lightBarriers(e,n),d=ho(this._renderPlanHass,this._renderDevices,null,this._virtualLights).filter(t=>t.device.space===e.id),p=new Map;for(const e of d){if(!e.device.id)continue;const t=p.get(e.device.id)||[];t.push(e),p.set(e.device.id,t)}const u=[],_=new Set;for(const t of this._renderDevices){if(t.space!==e.id)continue;const o=po(p.get(t.id)||[]);if(!o)continue;const d=`${e.id}|${t.id}`;_.add(d);const m=$s(o.passive?{state:o.on?"on":"off",attributes:{}}:this._renderPlanHass.states[o.eid],t.marker?.glow_color,i.glow_light.c),g=Number(t.marker?.glow_radius_cm),f=Number.isFinite(g)&&g>0?g/this._cellCm*this._gridPitch:s,v=this._pos(t);if(fh([v.x,v.y],c,h)){this._forgetGlowSource(d);continue}const y=this._glowTransition(d,!!m);if(!y)continue;m&&this._glowLastAppearance.set(d,{c:m.c,alpha:Ss(m.bri,i.glow_light.a)});const b=this._glowLastAppearance.get(d);if(!b)continue;let w=null;const k=`${e.id}|${l}|${v.x.toFixed(4)},${v.y.toFixed(4)}|${f.toFixed(4)}`,x=Jf(this._glowClipCache,k);if(x.hit)w=x.value;else{const t=Wu([v.x,v.y],f,a,96);w={lit:t.length>=3?dh([t],r,{onBoundsFailure:({boundIndex:t,phase:i})=>{const s=n[t]?.r;this._warnGlowGeometryFallback(e.id,l,s?.id||`#${t}`,i)}}):[]},Xf(this._glowClipCache,k,w,256)}u.push({key:d,sourceEid:o.eid,domId:y.domId,entering:y.entering,leaving:y.leaving,pos:v,c:b.c,alpha:b.alpha,geometry:w,r:f})}const m=`${e.id}|`;for(const e of this._glowRenderedSources.keys())e.startsWith(m)&&!_.has(e)&&this._forgetGlowSource(e);if(!u.length)return j``;const g=this._spaceWalls,f=o.length===n.length?[]:this._openCuts(),v=this._wallUnionGeometry()?.roomGeom,y=o.length===n.length?null:o.map(({r:t,poly:i})=>{const s=g.length&&t.id&&this._innerRoomContour(e,t.id,f,v)||i,o=this._cleanFloor(t,s,e).path,a=Pi(s,n.filter(e=>e.r!==t).map(e=>e.poly)),r=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||r(s),...a.map(r)].join(" ")}),b=this._viewOr(this._baseVb()),w=this._stageEl?.clientWidth&&b.w?this._stageEl.clientWidth/b.w:1,k=1/(w>0?w:1),x=!this._pinchStart&&!this._panStart&&Date.now()>=this._glowFeatherSuspendUntil;(null==this._glowFeatherUnits||x)&&(this._glowFeatherUnits=k);const $=this._glowFeatherUnits??k,S=4*$,M=u.reduce((e,t)=>({x:Math.min(e.x,t.pos.x-t.r-S),y:Math.min(e.y,t.pos.y-t.r-S),maxX:Math.max(e.maxX,t.pos.x+t.r+S),maxY:Math.max(e.maxY,t.pos.y+t.r+S),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});return M.w=M.maxX-M.x,M.h=M.maxY-M.y,j` ${Me(u,e=>e.key,e=>{const t=e.domId,i=e.geometry;return j` ${""} - ${cv.map(([t,i])=>j` + ${hv.map(([t,i])=>j` `)} @@ -5729,7 +5729,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${this._t("gs.glow_radius")} {const t=Kf(e.target.value);null!=t&&t>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:t})}} /> + @input=${e=>{const t=Yf(e.target.value);null!=t&&t>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:t})}} /> ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")} @@ -5804,7 +5804,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 `:G} -
${this._t("gs.about_version",{v:qf})}
+
${this._t("gs.about_version",{v:Wf})}
${this._t("gs.about_github")} @@ -5861,7 +5861,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${e.busy?"…":this._t("btn.save")} -
`}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(ov,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>W` +
`}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(av,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>W`
${this._rangeInput(50,300,5,Math.round(100*e[t]),e=>this._saveKioskScale({[t]:e/100}))} ${Math.round(100*e[t])}% @@ -5918,7 +5918,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${this._spaceDialog?this._renderSpaceDialog():G} ${this._importDialog?this._renderImportDialog():G} ${this._toast?W``:G} - `;const s=this._spaceModel();if(!s)return G;const n="valid"===i.kind?[s]:e,o=s.vb,a=this._effectiveProjection();this._renderProjection=a,this._labs.active.length&&function(e){Vg||Qg(e);const t=Gg,i=t.active.join(",");if(!i||i===Yg)return;Yg=i;const s=new Map(Ng.map(e=>[e.id,e])),n=t.active.map(e=>{const t=s.get(e);return`${e} (#${t.issue}, expires ${t.expires})`}).join(", ");console.info(`HOUSEPLAN LABS: ${n}`)}(qf);const r="iso"===a,l="devices"===this._mode&&this._showAll,c=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||l)),h=this._renderDeviceSnapshot,d=this._spaceDisplayForRender(),p=r?this._isoDecorationLayers(d):null,u=p?this._isoOpeningPanels(p):[],_=this._resolvedRoomFills(s,d),m=this._resolvedGlowBase(s,d,_),g=d.showLqi??this._config.show_signal??!0,f=this._config.icon_size??2.5,v=f>8?2.5:f,y=d_(v),b=this._viewOr(this._baseVb()),w=this._floorView(b),k=this._editing?"":this._stageBg(d),x=this._opMeasureView,$=this._decorMeasure,S=this._bdLive,M=this._furnLive,C="view"===this._mode?this._editorChromeMode:this._mode,D=this._roomHoverPaths(s),P=s.bg?this._display(s.bg.href):"",T=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,I=this._modeTransitionVisual,R=this._dayCycleState(),A=I?.viewWeight??("view"===this._mode?1:0),z=this._modeTransition.state?.from.presentedMode,F=!this._markup||!!I&&("view"===I.presentedMode||"devices"===I.presentedMode||"view"===z||"devices"===z),E=I?.stageColor||k,N=I?.sceneBrightness??1;return W` + `;const s=this._spaceModel();if(!s)return G;const n="valid"===i.kind?[s]:e,o=s.vb,a=this._effectiveProjection();this._renderProjection=a,this._labs.active.length&&function(e){Kg||ef(e);const t=Vg,i=t.active.join(",");if(!i||i===Jg)return;Jg=i;const s=new Map(Og.map(e=>[e.id,e])),n=t.active.map(e=>{const t=s.get(e);return`${e} (#${t.issue}, expires ${t.expires})`}).join(", ");console.info(`HOUSEPLAN LABS: ${n}`)}(Wf);const r="iso"===a,l="devices"===this._mode&&this._showAll,c=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||l)),h=this._renderDeviceSnapshot,d=this._spaceDisplayForRender(),p=r?this._isoDecorationLayers(d):null,u=p?this._isoOpeningPanels(p):[],_=this._resolvedRoomFills(s,d),m=this._resolvedGlowBase(s,d,_),g=d.showLqi??this._config.show_signal??!0,f=this._config.icon_size??2.5,v=f>8?2.5:f,y=p_(v),b=this._viewOr(this._baseVb()),w=this._floorView(b),k=this._editing?"":this._stageBg(d),x=this._opMeasureView,$=this._decorMeasure,S=this._bdLive,M=this._furnLive,C="view"===this._mode?this._editorChromeMode:this._mode,D=this._roomHoverPaths(s),P=s.bg?this._display(s.bg.href):"",T=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,I=this._modeTransitionVisual,R=this._dayCycleState(),A=I?.viewWeight??("view"===this._mode?1:0),z=this._modeTransition.state?.from.presentedMode,F=!this._markup||!!I&&("view"===I.presentedMode||"devices"===I.presentedMode||"view"===z||"devices"===z),E=I?.stageColor||k,N=I?.sceneBrightness??1;return W` + transform=${p?.structural?af():G}> ${""} ${this._wallHatchDefs(d.color)}${j`${this._paperShapes(s.rooms).map(e=>"path"in e?j``:"poly"in e?j``:j``)}`} ${this._editing?this._renderMarkupDefs(o):G} @@ -6039,7 +6039,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:G} @dblclick=${e=>this._openBackdropDialog(e)} preserveAspectRatio="none" />`:G} - ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,o=e=>(n.has(e)||n.set(e,hi(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||d.showBorders).map(n=>{let a="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(a+=" picked");let r="";const l=zs(d.fill,n);if(!this._markup&&(d.showBorders||"none"!==l)){a+=" styled";const e=[];e.push(`--room-stroke:${d.color}`,`--room-stroke-op:${d.showBorders?d.opacity:0}`);const t=_.byRoom.get(n)||null;t?(a+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),r=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",g?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},p=o(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),m=p&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return p.some((e,i)=>Hs(t,[e[0],e[1],...p[(i+1)%p.length]])<=.08*this._gridPitch)}):[],f=u?[]:t,v=m.concat(f);v.length&&(a+=" noedge");const y=this._spaceWalls.length&&n.id&&p&&this._innerRoomContour(s,n.id,e,i)||p,b=y?Pi(y,(w=n,s.rooms.filter(e=>e!==w).map(o).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=y?this._cleanFloor(n,y,s).path:"",$=n.id||G,S=n.area||G,M=x&&y?j`{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,o=e=>(n.has(e)||n.set(e,hi(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||d.showBorders).map(n=>{let a="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(a+=" picked");let r="";const l=zs(d.fill,n);if(!this._markup&&(d.showBorders||"none"!==l)){a+=" styled";const e=[];e.push(`--room-stroke:${d.color}`,`--room-stroke-op:${d.showBorders?d.opacity:0}`);const t=_.byRoom.get(n)||null;t?(a+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),r=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",g?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},p=o(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),m=p&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return p.some((e,i)=>Ls(t,[e[0],e[1],...p[(i+1)%p.length]])<=.08*this._gridPitch)}):[],f=u?[]:t,v=m.concat(f);v.length&&(a+=" noedge");const y=this._spaceWalls.length&&n.id&&p&&this._innerRoomContour(s,n.id,e,i)||p,b=y?Pi(y,(w=n,s.rooms.filter(e=>e!==w).map(o).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=y?this._cleanFloor(n,y,s).path:"",$=n.id||G,S=n.area||G,M=x&&y?j`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @@ -6226,7 +6226,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 `:G} ${this._toast?W``:G} - `}_vacCandidateStatus(e,t,i=this._planHass){const s=this._bindingStatus("entity:"+e);if("ha_disabled"===s.kind)return"disabled";if("orphaned"===s.kind)return"missing";if("unverified"===s.kind)return"unverified";const n=i?.states?.[e];return"unavailable"===n?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,s]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const n=jd(e,s,t[e]);n&&i.push(n)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const s=e.marker?.vacuum,n="string"==typeof s?.source&&!!s.source,o=new Set(e.entities||[]),a=new Set(o);n&&a.add(s.source);const r=i?.entities||{},l=[];for(const e of a){const t=i?.states?.[e],o=jd(e,t,r[e]);o?l.push(o):n&&e===s.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:r[e]?.platform?String(r[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,s){const n="string"==typeof e&&e.length>0,o=new Set(t),a=Array.from(i).sort((t,i)=>n&&t.entityId===e?-1:n&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(n){const t=a.find(t=>t.entityId===e);return{entityId:e,status:s[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:a}}const r=a.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===s[e.entityId]);return r?{entityId:r.entityId,status:"ok",pinned:!1,candidates:a}:{entityId:null,status:"none",pinned:!1,candidates:a}}(s?.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=ho(this._planHass,this._devices,null,this._virtualLights)){const i=t_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:n_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=O_(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),L_(s,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=ho(this._planHass,this._devices,null,this._virtualLights);for(const s of this._devices){if(s.hidden)continue;if("icon_ripple"!==Bi(s.marker?.display))continue;t.add(s.id);const n=this._activitySnapshot(s,i);e.set(s.id,n);const{samples:o,sourceKey:a}=n;let r=this._activityRt.get(s.id);r?r.sources!==a&&H_(r,a,o,window.clearTimeout.bind(window)):(r=O_(a,o),this._activityRt.set(s.id,r))}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"!==Bi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:s,sourceKey:n}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==n)continue;const a=B_(o,s,window.clearTimeout.bind(window));a&&this._stampActivity(t.id,a,n)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===Bi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),s=Kd(this.hass.states[i||""]?.state),n=Wd(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)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const a="never"!==Qd(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const r=n?.pos;if(s&&r){const e=r.x+":"+r.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=[r.x,r.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 s={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(s),s}_renderVacSection(e){const t=this._devices.find(t=>t.id===e.devId);if(!t||!this._isVacDev(t))return G;const i=t.marker?.vacuum||{},s=this._vacAllCamerasFor===t.id,n=this._vacSourceResolution(t,s),o=n.entityId,a=n.candidates.find(e=>e.entityId===o)||null,r=o?Wd(this.hass?.states[o]?.attributes):null,l=!1!==i.live&&("ok"===n.status||"unsupported"===n.status),c=this._vacPlanRoomAnchors(t.space),h=r?function(e,t){const i=new Set(t.map(Ud).filter(Boolean)),s=new Set;for(const t of e){const e=Ud(t.name||"");e&&i.has(e)&&s.add(e)}return s.size}(r.rooms,c.map(e=>e.name)):0,d=!!(l&&r&&h>=3),p=Object.keys(i.calibration||{}),u=e=>{const i=this._vacEnsureMarker(t);i&&(i.vacuum={...i.vacuum||{},...e},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate())},_=new Set(t.entities||[]),m=n.candidates.filter(e=>_.has(e.entityId)||e.entityId===i.source),g=s?n.candidates.filter(e=>e.entityId.startsWith("camera.")&&!m.some(t=>t.entityId===e.entityId)):[],f=e=>[e.hasPosition?this._t("vac.cap_position"):"",e.hasRooms?this._t("vac.cap_rooms_short"):"",e.hasPath?this._t("vac.cap_path"):"",e.hasMapId?this._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this._t("vac.cap_none"),v=e=>W` + `}_vacCandidateStatus(e,t,i=this._planHass){const s=this._bindingStatus("entity:"+e);if("ha_disabled"===s.kind)return"disabled";if("orphaned"===s.kind)return"missing";if("unverified"===s.kind)return"unverified";const n=i?.states?.[e];return"unavailable"===n?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,s]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const n=jd(e,s,t[e]);n&&i.push(n)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const s=e.marker?.vacuum,n="string"==typeof s?.source&&!!s.source,o=new Set(e.entities||[]),a=new Set(o);n&&a.add(s.source);const r=i?.entities||{},l=[];for(const e of a){const t=i?.states?.[e],o=jd(e,t,r[e]);o?l.push(o):n&&e===s.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:r[e]?.platform?String(r[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,s){const n="string"==typeof e&&e.length>0,o=new Set(t),a=Array.from(i).sort((t,i)=>n&&t.entityId===e?-1:n&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(n){const t=a.find(t=>t.entityId===e);return{entityId:e,status:s[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:a}}const r=a.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===s[e.entityId]);return r?{entityId:r.entityId,status:"ok",pinned:!1,candidates:a}:{entityId:null,status:"none",pinned:!1,candidates:a}}(s?.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=ho(this._planHass,this._devices,null,this._virtualLights)){const i=i_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:o_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=L_(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),B_(s,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=ho(this._planHass,this._devices,null,this._virtualLights);for(const s of this._devices){if(s.hidden)continue;if("icon_ripple"!==Bi(s.marker?.display))continue;t.add(s.id);const n=this._activitySnapshot(s,i);e.set(s.id,n);const{samples:o,sourceKey:a}=n;let r=this._activityRt.get(s.id);r?r.sources!==a&&H_(r,a,o,window.clearTimeout.bind(window)):(r=L_(a,o),this._activityRt.set(s.id,r))}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"!==Bi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:s,sourceKey:n}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==n)continue;const a=q_(o,s,window.clearTimeout.bind(window));a&&this._stampActivity(t.id,a,n)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===Bi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),s=Kd(this.hass.states[i||""]?.state),n=Wd(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)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const a="never"!==Qd(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const r=n?.pos;if(s&&r){const e=r.x+":"+r.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=[r.x,r.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 s={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(s),s}_renderVacSection(e){const t=this._devices.find(t=>t.id===e.devId);if(!t||!this._isVacDev(t))return G;const i=t.marker?.vacuum||{},s=this._vacAllCamerasFor===t.id,n=this._vacSourceResolution(t,s),o=n.entityId,a=n.candidates.find(e=>e.entityId===o)||null,r=o?Wd(this.hass?.states[o]?.attributes):null,l=!1!==i.live&&("ok"===n.status||"unsupported"===n.status),c=this._vacPlanRoomAnchors(t.space),h=r?function(e,t){const i=new Set(t.map(Ud).filter(Boolean)),s=new Set;for(const t of e){const e=Ud(t.name||"");e&&i.has(e)&&s.add(e)}return s.size}(r.rooms,c.map(e=>e.name)):0,d=!!(l&&r&&h>=3),p=Object.keys(i.calibration||{}),u=e=>{const i=this._vacEnsureMarker(t);i&&(i.vacuum={...i.vacuum||{},...e},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate())},_=new Set(t.entities||[]),m=n.candidates.filter(e=>_.has(e.entityId)||e.entityId===i.source),g=s?n.candidates.filter(e=>e.entityId.startsWith("camera.")&&!m.some(t=>t.entityId===e.entityId)):[],f=e=>[e.hasPosition?this._t("vac.cap_position"):"",e.hasRooms?this._t("vac.cap_rooms_short"):"",e.hasPath?this._t("vac.cap_path"):"",e.hasMapId?this._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this._t("vac.cap_none"),v=e=>W`
`)}return this._vacLastView=t,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?W` ${o.length?j`${o}`:G} - ${n}`:G}_renderDevice(e,t,i=!0){const s=this._pos(e),n=this._scenePoint([s.x,s.y]),o=(n[0]-t.x)/t.w*100,a=(n[1]-t.y)/t.h*100,r=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${a}%`,...Go(r)],c=r.disabledReason,h=r.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Yu(r),p="view"===this._mode||"devices"===this._mode,u=[h,r.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==r.pulse.kind?this._t(`marker.pulse_a11y_${r.pulse.reason}`):"",r.valueFullText||r.valueText||"",Io(r.valueBadge),null!=r.lqiText&&r.lqiBand?this._t(`marker.lqi_a11y_${r.lqiBand}`,{value:r.lqiText}):""].filter(Boolean).join(", "),_=[e.model,r.valueBadge?.fullText||"",null!=r.lqiText?"LQI "+r.lqiText:""].filter(Boolean).join(" · ");return W`
`}_roomArea(e){const t=hi(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const s=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,n=this._cleanFloor(e,s),o=this._cellCm/this._gridPitch;return ir(n.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;return t?Mo(this._renderPlanHass,t,"temp",this._markers):e.area?this._climate().get(e.area)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;return t?Mo(this._renderPlanHass,t,"hum",this._markers):e.area?this._climate().get(e.area)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const s=function(e,t,i){const s=new Map;if(!e?.entities)return s;const n=wo(i),o=new Set;for(const e of i||[]){if(e?.removed||!0!==e?.use_climate_temp)continue;const t=(e.binding||"").indexOf(":");t>0&&o.add(e.binding.slice(t+1))}const a=new Map;for(const[t,i]of Object.entries(e.entities)){if(i.device_id&&n.devices.has(i.device_id)&&!n.liveEntities.has(t)||!i.device_id&&n.entities.has(t)&&!n.liveEntities.has(t))continue;const s=i.device_id?e.devices?.[i.device_id]:null,r=i.area_id||s?.area_id||null;if(!r)continue;if(i.entity_category)continue;if(!(o.size>0&&t.startsWith("climate.")&&(o.has(t)||i.device_id&&o.has(i.device_id)))){if(Ye.has(i.platform))continue;if(Co.test(t))continue}let l=a.get(r);l||(l=new Map,a.set(r,l));const c=i.device_id||t;let h=l.get(c);if(!h){const n=e.states?.[t];h={name:(s?s.name_by_user||s.name:i.name||n?.attributes?.friendly_name||t)||t,model:s?.model,ents:[]},l.set(c,h)}h.ents.push(t)}for(const[i,n]of a){const a=[],r=[];for(const[i,s]of n){const n=bo(e,s.name,s.model,s.ents,t),l="mdi:thermometer"===n||"mdi:air-filter"===n;if(l){const t=mo(e,s.ents);null!=t&&a.push(t)}if(o.size>0&&(o.has(i)||s.ents.some(e=>o.has(e)))){const t=go(e,s.ents);null!=t&&a.push(t)}if(l||"mdi:water-percent"===n){const t=vo(e,s.ents);null!=t&&r.push(t)}}(a.length||r.length)&&s.set(i,{temp:a.length?Math.round(a.reduce((e,t)=>e+t,0)/a.length*10)/10:null,hum:r.length?Math.round(r.reduce((e,t)=>e+t,0)/r.length):null})}return s}(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:s},s}_resetRoomDialogFields(){this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1}_openRoomEdit(e){if(!e.id)return;this._roomEditId=e.id,this._nameSel=e.name||"",this._areaSel=e.area||"",this._roomFill="glow"===e.settings?.fill_mode?"":e.settings?.fill_mode||"";const t=e.settings?.custom_fill;this._roomCustomFill=t&&"object"==typeof t?rs(t,cs(this._curSpaceCfg).customFill):null,this._roomTempSrc=e.settings?.temp_source||"",this._roomHumSrc=e.settings?.hum_source||"",this._roomNameScale=Es(e.settings?.name_scale),this._roomLabelScale=Es(e.settings?.label_scale),this._roomSrcOpen=null,this._roomSrcFilter="",this._roomDialog=!0}_roomSettingsFromDialog(){const e={};return this._roomFill&&(e.fill_mode=this._roomFill),this._roomCustomFill&&(e.custom_fill=this._roomCustomFill),this._roomTempSrc&&(e.temp_source=this._roomTempSrc),this._roomHumSrc&&(e.hum_source=this._roomHumSrc),1!==this._roomNameScale&&(e.name_scale=this._roomNameScale),1!==this._roomLabelScale&&(e.label_scale=this._roomLabelScale),Object.keys(e).length?e:null}_saveRoomEdit(){const e=this._curSpaceCfg,t=e?.rooms.find(e=>e.id===this._roomEditId);if(!t)return this._roomDialog=!1,void(this._roomEditId=null);t.name=this._nameSel.trim()||t.name,t.area=this._areaSel||null;const i=t.settings||{},s={...i};this._roomFill?s.fill_mode=this._roomFill:delete s.fill_mode,this._roomCustomFill?s.custom_fill=this._roomCustomFill:delete s.custom_fill,"glow"===i.fill_mode&&"boolean"!=typeof i.glow&&(s.glow=!0),this._roomTempSrc?s.temp_source=this._roomTempSrc:delete s.temp_source,this._roomHumSrc?s.hum_source=this._roomHumSrc:delete s.hum_source,1!==this._roomNameScale?s.name_scale=this._roomNameScale:delete s.name_scale,1!==this._roomLabelScale?s.label_scale=this._roomLabelScale:delete s.label_scale,Object.keys(s).length?t.settings=s:delete t.settings,this._saveConfig(),this._roomDialog=!1,this._roomEditId=null,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate(),this._showToast(this._t("toast.room_updated"))}_roomSrcCandidates(){const e=this._planHass,t=wo(this._markers),i=this._roomSrcFilter.trim().toLowerCase(),s=[];for(const n of Object.values(e.devices)){if("service"===n.entry_type)continue;if(t.devices.has(n.id))continue;const e=(n.name_by_user||n.name||n.id).trim();i&&!e.toLowerCase().includes(i)||s.push({value:"device:"+n.id,label:e,sub:n.model||this._t("marker.sub_device")})}for(const[n,o]of Object.entries(e.entities)){if(!n.startsWith("sensor.")||o.hidden)continue;if(ko(e,n,t))continue;const a=o.name||e.states[n]?.attributes?.friendly_name||n;i&&!(a+" "+n).toLowerCase().includes(i)||s.push({value:"entity:"+n,label:a,sub:n})}return s.sort((e,t)=>e.label.localeCompare(t.label)),s.slice(0,200)}_roomSrcLabel(e){const t=e.indexOf(":"),i=e.slice(0,t),s=e.slice(t+1);return"device"===i?this._fullRegistryHass.devices[s]?.name_by_user||this._fullRegistryHass.devices[s]?.name||s:this._fullRegistryHass.entities[s]?.name||this.hass.states[s]?.attributes?.friendly_name||s}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*lv,y:i.y*lv};const s=this._snap(this._roomCenter(e));return{x:s[0],y:s[1]}}_labelDown(e,t,i){if("plan"!==this._mode)return;e.preventDefault(),e.stopPropagation();const s=this._labelPos(t,i);this._drag={id:"rl_"+(t.id||""),sx:e.clientX,sy:e.clientY,ox:s.x,oy:s.y,moved:!1},gv(e),this._tip=null}_labelMove(e,t,i){const s="rl_"+(t.id||"");if(!this._drag||this._drag.id!==s)return;const n=this._stageEl;if(!n)return;const o=this._spaceModelById(i);if(!o)return;const a=o.vb,r=n.getBoundingClientRect(),l=this._viewOr(a),c=(e.clientX-this._drag.sx)/r.width*l.w,h=(e.clientY-this._drag.sy)/r.height*l.h;Math.abs(e.clientX-this._drag.sx)+Math.abs(e.clientY-this._drag.sy)>3&&(this._drag.moved=!0);const d=Oh(this._drag.ox+c),p=Oh(this._drag.oy+h);this._savePos({id:s,space:i},d,p)}_labelUp(e){const t="rl_"+(e.id||"");if(!this._drag||this._drag.id!==t)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&window.setTimeout(()=>this._drag=null,0)}_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){if("plan"!==this._mode)return;e.preventDefault(),e.stopPropagation();const s=e.target.closest(".roomlabel");if(!s)return;const n=s.getBoundingClientRect(),o=n.left+n.width/2,a=n.top+n.height/2,r=Math.max(8,Math.hypot(e.clientX-o,e.clientY-a));this._rlResize={id:"rl_"+(t.id||""),space:i,k0:this._labelScale(t),cx:o,cy:a,d0:r},gv(e)}_rlResizeMove(e){const t=this._rlResize;if(!t)return;e.stopPropagation();const i=Math.max(8,Math.hypot(e.clientX-t.cx,e.clientY-t.cy)),s=Math.min(3,Math.max(.5,t.k0*(i/t.d0))),n=this._layout[t.id];if(n)this._layout={...this._layout,[t.id]:{...n,k:s}};else{const e=t.id.slice(3),i=this._spaceModelById(t.space);if(!i)return;const n=i.rooms.find(t=>t.id===e);if(!n)return;const o=this._labelPos(n,t.space);this._layout={...this._layout,[t.id]:{s:t.space,x:o.x/lv,y:o.y/lv,k:s}}}this._dirtyPos.add(t.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(e,t,i){if(!e.id)return G;let s=null;if(e.poly?(s=this._gearPtCache.get(e.poly)||null,s||(s=$i(e.poly),this._gearPtCache.set(e.poly,s))):null!=e.x&&null!=e.y&&(s=[e.x+(e.w||0)/2,e.y+(e.h||0)/2]),!s)return G;const n=(s[0]-i.x)/i.w*100,o=(s[1]-i.y)/i.h*100;return W`
`}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,s=(i[0]-e.x)/e.w*100,n=(i[1]-e.y)/e.h*100,o=Ns(t,i),a=Math.round(10*o)/10,r=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const s=Math.abs(t[0]-e[0]),n=Math.abs(t[1]-e[1]);if(!Number.isFinite(s)||!Number.isFinite(n)||Math.hypot(s,n)<=i)return!1;const o=Math.max(s,n,1);return s<=i*o||n<=i*o||Math.abs(s-n)<=i*o}(t,i,2e-4*this._gridPitch);return W`
- ${this._fmtLen(t,i)} · ${a}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[s,n]=e.b;if(Math.abs(t-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(t+s)/2,a=(i+n)/2;if("line"===e.kind){const t=Ns(e.a,e.b);return{x:o,y:a,on45:Os(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const r=this._fmtLen([t,i],[s,i]),l=this._fmtLen([s,i],[s,n]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(s-t)/2,0]),r=this._fmtLen([0,0],[0,Math.abs(n-i)/2]);return{x:o,y:a,on45:!1,text:Math.abs(Math.abs(s-t)-Math.abs(n-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${r}`}}return{x:o,y:a,on45:!1,text:`${r} × ${l} · ${ir(Br(Math.abs(s-t),this._cellCm,this._gridPitch)*Br(Math.abs(n-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._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.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=lv,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){const e=[],t=this._spaceModel();if(this._markup){if(!t)return e;if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of t.rooms){if(!s.name||s.id===i)continue;const t=this._labelPos(s,this._space);e.push([t.x,t.y])}return e}for(const i of t.rooms){const t=hi(i);if(t)for(const i of t)e.push(i)}if("draw"===this._tool)for(const t of this._path)e.push(t);if("split"===this._tool&&this._splitSel?.pts)for(const t of this._splitSel.pts)e.push(t);return e}if("devices"===this._mode){for(const t of this._devices){if(t.space!==this._space||t.id===this._drag?.id||"ha_disabled"===t.bindingStatus?.kind)continue;const i=this._pos(t);e.push([i.x,i.y])}return e}if("decor"===this._mode){const t=this._decorMove?.id;return e.push(...this._decorSnapGeometry(t).points),this._decorDraft&&e.push(this._decorDraft.a),e}return e}_renderAlignGuides(){const e=this._alignPoint;if(!e)return j``;const t=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(e,t,i){let s=null,n=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&&(!s||t1e-6&&(!n||t + ${this._fmtLen(t,i)} · ${a}°`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[s,n]=e.b;if(Math.abs(t-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(t+s)/2,a=(i+n)/2;if("line"===e.kind){const t=Ns(e.a,e.b);return{x:o,y:a,on45:Os(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const r=this._fmtLen([t,i],[s,i]),l=this._fmtLen([s,i],[s,n]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(s-t)/2,0]),r=this._fmtLen([0,0],[0,Math.abs(n-i)/2]);return{x:o,y:a,on45:!1,text:Math.abs(Math.abs(s-t)-Math.abs(n-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${r}`}}return{x:o,y:a,on45:!1,text:`${r} × ${l} · ${ir(Br(Math.abs(s-t),this._cellCm,this._gridPitch)*Br(Math.abs(n-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._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.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=cv,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){const e=[],t=this._spaceModel();if(this._markup){if(!t)return e;if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of t.rooms){if(!s.name||s.id===i)continue;const t=this._labelPos(s,this._space);e.push([t.x,t.y])}return e}for(const i of t.rooms){const t=hi(i);if(t)for(const i of t)e.push(i)}if("draw"===this._tool)for(const t of this._path)e.push(t);if("split"===this._tool&&this._splitSel?.pts)for(const t of this._splitSel.pts)e.push(t);return e}if("devices"===this._mode){for(const t of this._devices){if(t.space!==this._space||t.id===this._drag?.id||"ha_disabled"===t.bindingStatus?.kind)continue;const i=this._pos(t);e.push([i.x,i.y])}return e}if("decor"===this._mode){const t=this._decorMove?.id;return e.push(...this._decorSnapGeometry(t).points),this._decorDraft&&e.push(this._decorDraft.a),e}return e}_renderAlignGuides(){const e=this._alignPoint;if(!e)return j``;const t=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(e,t,i){let s=null,n=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&&(!s||t1e-6&&(!n||t ${i.map(t=>{const[i,o,a,r]="x"===t.axis?[t.at,t.from[1],t.at,e[1]+Math.sign(e[1]-t.from[1])*n]:[t.from[0],t.at,e[0]+Math.sign(e[0]-t.from[0])*n,t.at];return j` `})} @@ -6438,7 +6438,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 - `}_renderOpeningDialog(){const e=this._openingDialog,t=this._spaceModel()?.partitions||[],i=e.id?this._curSpaceCfg?.openings?.find(t=>t.id===e.id):null,s={id:e.id||"preview",type:e.type,x:e.x/lv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/lv,...e.host?{host:e.host}:{}},n=Rp(i,s),o=e.host?n?Ip(s,t,lv,this._cellCm,this._gridPitch):Tp(s,t,lv,this._cellCm,this._gridPitch):null,a="does-not-fit-jamb"===o?.reason,r=e.host?t.find(t=>t.id===e.host.id):null,l=r?li(r.cm/2,this._imperial):"",c=!!e.host&&!o?.resolved&&!a,h="gate"===e.type?"mdi:gate":"window"===e.type?"mdi:window-closed-variant":"passage"===e.type?"mdi:arch":"mdi:door",d=(t,i)=>{const s="contact"===t?e.contact:e.lock,n="contact"===t?!!e.contactOpen:!!e.lockOpen,o="contact"===t?e.contactFilter||"":e.lockFilter||"",a=i.find(e=>e.value===s),r=a?.label||this.hass.states[s]?.attributes?.friendly_name||this._fullRegistryHass.entities[s]?.name||s,l=function(e,t,i=200){const s=t.trim().toLowerCase();return(s?e.filter(e=>e.label.toLowerCase().includes(s)||e.value.toLowerCase().includes(s)):e).slice(0,Math.max(0,i))}(i,o);return W` + `}_renderOpeningDialog(){const e=this._openingDialog,t=this._spaceModel()?.partitions||[],i=e.id?this._curSpaceCfg?.openings?.find(t=>t.id===e.id):null,s={id:e.id||"preview",type:e.type,x:e.x/cv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/cv,...e.host?{host:e.host}:{}},n=Rp(i,s),o=e.host?n?Ip(s,t,cv,this._cellCm,this._gridPitch):Tp(s,t,cv,this._cellCm,this._gridPitch):null,a="does-not-fit-jamb"===o?.reason,r=e.host?t.find(t=>t.id===e.host.id):null,l=r?li(r.cm/2,this._imperial):"",c=!!e.host&&!o?.resolved&&!a,h="gate"===e.type?"mdi:gate":"window"===e.type?"mdi:window-closed-variant":"passage"===e.type?"mdi:arch":"mdi:door",d=(t,i)=>{const s="contact"===t?e.contact:e.lock,n="contact"===t?!!e.contactOpen:!!e.lockOpen,o="contact"===t?e.contactFilter||"":e.lockFilter||"",a=i.find(e=>e.value===s),r=a?.label||this.hass.states[s]?.attributes?.friendly_name||this._fullRegistryHass.entities[s]?.name||s,l=function(e,t,i=200){const s=t.trim().toLowerCase();return(s?e.filter(e=>e.label.toLowerCase().includes(s)||e.value.toLowerCase().includes(s)):e).slice(0,Math.max(0,i))}(i,o);return W`
`:G} - `}_markerValueBadgeFields(e){return(t={touched:e.valueBadgeTouched,originalHas:e.originalHasValueBadge,original:e.originalValueBadge,enabled:e.valueBadgeEnabled,source:e.valueBadgeSource,position:e.valueBadgePosition}).touched?{value_badge:{enabled:t.enabled,source:t.source,position:t.position}}:t.originalHas?{value_badge:t.original}:{};var t}_markerDraft(e){if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return null;const t=qs(e.room),i=Fi(e.binding,e.devId,()=>"__hp_device_preview__"),s=this._markers.find(t=>t.id===i||t.id===e.devId),n=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),o=this._effectiveStoredTapAction(e),a={id:i,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle||null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===o&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:n.length?n:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Kf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:e.hideFromPlan,vacuum:s?.vacuum||null};return("virtual"===e.binding||e.room)&&(a.space=t?.space||("virtual"===e.binding?this._space:null),a.area=t?.area||null,a.room_id=t?.roomId||null),a}_markerPreviewDevice(e){const t=this._markerDraft(e);if(!t)return null;const i=`${this._haRegistry.revision}\n${Md(this._markers)}\n${JSON.stringify(t)}`;if(this._markerPreviewMemo?.key===i)return this._markerPreviewMemo.device;const s=function(e){const{marker:t,siblingMarkers:i=[],...s}=e;return So({...s,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),marker:t,siblingMarkers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||this._space,loc:e=>this._t(e),iconRules:this._iconRules});return this._markerPreviewMemo={key:i,device:s},s}_markerPreviewDevices(e){const t=this._markerPreviewDevicesMemo;if(t?.base===this._devices&&t.preview===e)return t.devices;const i=[...this._devices.filter(t=>t.id!==e.id),e];return this._markerPreviewDevicesMemo={base:this._devices,preview:e,devices:i},i}_toggleIntent(e,t=this._devices){return kp({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i=this._markerPreviewDevices(t);return this._toggleIntent(t,i)}_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 s={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 s?this._t(s):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(!$p(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,s=i?e.targets.filter(e=>"on"===e.state).length:0,n=i?0===s?t.groupAllOff():t.groupCurrent(s,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(n),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){if(!e)return[];const t=e=>this._t(`marker.toggle_effect_${e.replace("-","_")}`),i=e=>this._t(`marker.toggle_skip_${e.replace("-","_")}`);return function(e,t){const i=e.targets[0]||e.skippedTargets[0],s=[];return"group"===e.kind?e.targets.length&&s.push(t.group(e.targets)):i&&s.push(t.single(i)),e.nextEffect&&e.targets.length&&s.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&s.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&s.push(t.none(e.noneReason)),s}(e,{single:e=>{if("via"in e&&"virtual-light"===e.via)return this._t("marker.virtual_light_target",{name:e.name});const t=e.entityId||("ref"in e?e.ref:""),i=e.name||t;return this._t("marker.toggle_hint_single",{name:i,id:t})},group:e=>this._t("marker.toggle_hint_group",{count:e.length,names:e.map(e=>`${e.name} (${e.entityId})`).join(", ")}),currentNext:(e,i)=>"virtual-light"===e.via?this._t("marker.virtual_light_current",{state:this._t("on"===e.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:t(i)}):this._t("marker.toggle_hint_current",{state:this._toggleStateText(e.entityId,e.state),effect:t(i)}),groupCurrentNext:(e,i)=>this._t("marker.toggle_hint_group_current",{on:e.filter(e=>"on"===e.state).length,count:e.length,effect:t(i)}),skipped:e=>this._t("marker.toggle_hint_skipped",{count:e.length,targets:e.map(e=>{const t=e.entityId||e.ref;return`${e.name||t} (${t}: ${i(e.reason)})`}).join(", ")}),none:e=>this._t(`marker.toggle_none_${e.replaceAll("-","_")}`)})}_effectiveStoredTapAction(e,t){return e.tapActionTouched?e.tapAction:vp(e.originalHasTapAction?e.originalTapAction:null,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._effectiveStoredTapAction(e,t?.primary?.split(".")[0])}_announceToggleDraft(e){const t=this._markerPreviewDevice(e),i=e.tapActionTouched?e:{...e,tapAction:this._effectiveMarkerTapAction(e,t)},s="toggle"===i.tapAction?this._toggleHintLines(this._toggleIntentForDialog(i)).join(" "):"";return{...i,tapHintAnnouncement:s}}_valueBadgeForBinding(e,t){const i={...e,binding:t,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},s=this._markerPreviewDevice(i);if(!s)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const n=[...this._devices.filter(e=>e.id!==s.id),s],o=Wo(this._planHass,s,n),a=jo(this._planHass,s,o);return{valueBadgeEnabled:e.valueBadgeEnabled&&!!a,valueBadgeSource:a,valueBadgeTouched:!0}}_markerSpatialSource(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i={...t,hidden:!1};return po(ho(this._planHass,[...this._devices.filter(e=>e.id!==i.id),i],null,this._virtualLights).filter(e=>e.device.id===i.id))}_markerAutoHasSpatialSource(e){const t={...e,lightRole:"auto",lightRoleTouched:!0},i=this._markerPreviewDevice(t);return!!i&&lo(this._planHass,{...i})}_setMarkerLightRole(e){const t=this._markerDialog;t&&(this._markerDialog={...t,lightRole:e,lightRoleTouched:!0})}_controlRefInfo(e){if(!e.startsWith("marker:"))return{label:this.hass.states[e]?.attributes?.friendly_name||e,sub:e,icon:e.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this._planEntityAvailable(e)};const t=e.slice(7),i=this._markers.find(e=>e.id===t),s=this._devices.find(e=>e.id===t);if(!i||i.removed||!0!==i.is_light)return{label:this._t("marker.control_missing_label"),sub:`${this._t("marker.control_broken")} (${e})`,icon:"mdi:alert-outline",warning:!0};const n=this._serverCfg?.spaces.find(e=>e.id===(s?.space||i.space)),o=!!s&&eo(s).length>0;return{label:i.name||s?.name||t,sub:[n?.title||s?.space||i.space,i.room_id||s?.area,o?"":this._t("marker.control_passive")].filter(Boolean).join(" · "),icon:i.icon||s?.icon||"mdi:lightbulb-outline",warning:!0===i.hidden||!0===s?.hidden}}_valueBadgeCandidateLabel(e){const t=e.source;if("derived_lqi"===t.kind)return this._t("marker.value_badge_lqi");if("derived_marker_state"===t.kind)return this._t("marker.value_badge_marker_state",{name:e.label});if("entity_state"===t.kind)return this._t("marker.value_badge_state",{name:e.label});const i=this.hass.states[t.entity_id]?.attributes?.friendly_name||this._fullRegistryHass.entities[t.entity_id]?.name||t.entity_id;return this._t(`marker.value_badge_attr_${t.attribute}`,{name:i})}_controlCandidates(e){const t=this._markerDraft(e)?.id||e.devId||"",i=[],s=new Set;for(const e of this._markers){if(e.id===t||e.removed||e.hidden||!0!==e.is_light)continue;if("virtual"!==e.binding&&"active"!==this._bindingStatus(e.binding).kind)continue;const n=this._devices.find(t=>t.id===e.id);if(!n||n.hidden)continue;const o=this._controlRefInfo(`marker:${e.id}`);for(const e of eo(n))s.add(e);i.push({value:`marker:${e.id}`,label:o.label,sub:o.sub,icon:o.icon,search:`${o.label} ${o.sub} ${e.id} ${eo(n).join(" ")}`.toLowerCase()})}const n=Object.keys(this.hass.states||{}).filter(t=>Ms(t)&&!s.has(t)&&Xn(e.binding,[t],this._bindingEntities(e.binding)).length>0&&this._planEntityAvailable(t)).map(e=>{const t=this._controlRefInfo(e);return{value:e,label:t.label,sub:e,icon:t.icon,search:`${t.label} ${e}`.toLowerCase()}}),o=e.controlsFilter.trim().toLowerCase();return[...i,...n].filter(t=>!e.controls.includes(t.value)&&(!o||t.search.includes(o))).slice(0,12).map(({search:e,...t})=>t)}_addControlRef(e,t){if(t.startsWith("marker:")){const i=this._markerDraft(e)?.id||e.devId||"";if(!i)return void this._showToast(this._t("toast.marker_binding_required"));const s=t.slice(7),n=this._markerDraft(e);if(function(e,t,i){if(!t||t===i)return!0;const s=new Map;for(const t of e)s.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const n=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;n.has(e)||(n.add(e),o.push(...s.get(e)||[]))}return!1}(n?[...this._markers.filter(e=>e.id!==i),n]:this._markers,i,s))return void this._showToast(this._t("toast.marker_control_cycle"))}this._markerDialog=this._announceToggleDraft({...e,controls:[...e.controls,t],controlsFilter:""})}_setMarkerGlowMode(e){const t=this._markerDialog;if(!t)return;if("auto"===e)return void(this._markerDialog={...t,glowMode:e,glowTouched:!0});const i=this._markerSpatialSource(t),s=xs(i?this._planHass.states[i.eid]:void 0,null,this._fillColors.glow_light.c),n=!t.glowColorDrafted,o="fixed"===e&&!t.glowBrightnessDrafted;this._markerDialog={...t,glowMode:e,glowColor:n?s.c:t.glowColor,glowBrightness:o?Math.max(1,Math.round(100*s.bri)):t.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:t.glowBrightnessDrafted||"fixed"===e,glowTouched:!0}}_renderMarkerDialog(){const e=this._markerDialog,t="virtual"===e.bindingMode,i=this._bindingCandidates();this._bindingEntities(e.binding);const s=t?null:this._bindingStatus(e.binding),n=!t&&this._bindingHasHaPage(e.binding),o=this._markerPreviewDevice(e),a=this._effectiveMarkerTapAction(e,o),r=o?cs(this._serverCfg?.spaces.find(e=>e.id===o.space)):null,l=o?this._markerPreviewDevices(o):this._devices,c="toggle"===a&&o?this._toggleIntent(o,l):null,h=this._toggleHintLines(c),d=o?a_(this._planHass,o,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:r?.showLqi??!1!==this._config?.show_signal,designPreview:!0,activityRuntime:this._activityRt.get(o.id),lightDevices:l,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}):null,p=o?Wo(this._planHass,o,l):[],u=o?jo(this._planHass,o,p):null,_=e.valueBadgeTouched?e.valueBadgeEnabled:!!d?.valueBadge,m=e.valueBadgeTouched?e.valueBadgeSource:d?.valueBadge?.source||e.valueBadgeSource,g=e.valueBadgeTouched?e.valueBadgePosition:d?.valueBadge?.position||e.valueBadgePosition,f=Ao(m),v=!!m&&!p.some(e=>e.key===f),y=p.find(e=>e.key===f),b=d?.valueSource?d.valueSource.attribute?`attr:${d.valueSource.eid}:${d.valueSource.attribute}`:`state:${d.valueSource.eid}`:"",w=this._markerAutoHasSpatialSource(e),k=!!o&&function(e,t){return io(e,t).some(e=>!!e.eid)}(this._planHass,{...o}),x=function(e,t,i,s){const n="always"===e||"auto"===e&&t,o=n&&!i;return{sourceExists:n,fromSourceEnabled:n&&i,manualEnabled:n,radiusEnabled:n,passive:o,effectiveMode:o&&"auto"===s?"fixed":s}}(e.lightRole,w,k,e.glowMode),$=!x.sourceExists,S=!x.fromSourceEnabled,M=x.passive,C=x.effectiveMode,D="never"===e.lightRole?this._t("marker.glow_disabled_never"):"auto"!==e.lightRole||w?M?this._t("marker.glow_passive_hint"):this._t("marker.glow_disabled_no_entity"):this._t("marker.glow_disabled_auto"),P=o?eo(o):[],T=o&&to(o)||"",I=!!e.lightEntity&&!P.includes(e.lightEntity),R=o?up(o):[],A=!!e.toggleEntity&&!R.includes(e.toggleEntity),z="toggle"===a?this._toggleIntentForDialog({...e,toggleEntity:"",toggleEntityTouched:!0}):null,F=z?[...z.targets,...z.skippedTargets].map(e=>e.entityId||("ref"in e?e.ref:"")).filter(Boolean).join(", "):"",E=(()=>{if(t)return null;const s=i.find(t=>t.value===e.binding);if(s)return s.label;const[n,o]=e.binding.split(":");return"device"===n?this._fullRegistryHass.devices[o]?.name_by_user||this._fullRegistryHass.devices[o]?.name||o:this._fullRegistryHass.entities[o]?.name||this.hass.states[o]?.attributes?.friendly_name||o})();return W``}_markerValueBadgeFields(e){return(t={touched:e.valueBadgeTouched,originalHas:e.originalHasValueBadge,original:e.originalValueBadge,enabled:e.valueBadgeEnabled,source:e.valueBadgeSource,position:e.valueBadgePosition}).touched?{value_badge:{enabled:t.enabled,source:t.source,position:t.position}}:t.originalHas?{value_badge:t.original}:{};var t}_markerDraft(e){if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return null;const t=qs(e.room),i=Fi(e.binding,e.devId,()=>"__hp_device_preview__"),s=this._markers.find(t=>t.id===i||t.id===e.devId),n=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),o=this._effectiveStoredTapAction(e),a={id:i,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle||null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===o&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:n.length?n:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Yf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:e.hideFromPlan,vacuum:s?.vacuum||null};return("virtual"===e.binding||e.room)&&(a.space=t?.space||("virtual"===e.binding?this._space:null),a.area=t?.area||null,a.room_id=t?.roomId||null),a}_markerPreviewDevice(e){const t=this._markerDraft(e);if(!t)return null;const i=`${this._haRegistry.revision}\n${Md(this._markers)}\n${JSON.stringify(t)}`;if(this._markerPreviewMemo?.key===i)return this._markerPreviewMemo.device;const s=function(e){const{marker:t,siblingMarkers:i=[],...s}=e;return So({...s,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),marker:t,siblingMarkers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||this._space,loc:e=>this._t(e),iconRules:this._iconRules});return this._markerPreviewMemo={key:i,device:s},s}_markerPreviewDevices(e){const t=this._markerPreviewDevicesMemo;if(t?.base===this._devices&&t.preview===e)return t.devices;const i=[...this._devices.filter(t=>t.id!==e.id),e];return this._markerPreviewDevicesMemo={base:this._devices,preview:e,devices:i},i}_toggleIntent(e,t=this._devices){return kp({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i=this._markerPreviewDevices(t);return this._toggleIntent(t,i)}_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 s={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 s?this._t(s):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(!$p(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,s=i?e.targets.filter(e=>"on"===e.state).length:0,n=i?0===s?t.groupAllOff():t.groupCurrent(s,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(n),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){if(!e)return[];const t=e=>this._t(`marker.toggle_effect_${e.replace("-","_")}`),i=e=>this._t(`marker.toggle_skip_${e.replace("-","_")}`);return function(e,t){const i=e.targets[0]||e.skippedTargets[0],s=[];return"group"===e.kind?e.targets.length&&s.push(t.group(e.targets)):i&&s.push(t.single(i)),e.nextEffect&&e.targets.length&&s.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&s.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&s.push(t.none(e.noneReason)),s}(e,{single:e=>{if("via"in e&&"virtual-light"===e.via)return this._t("marker.virtual_light_target",{name:e.name});const t=e.entityId||("ref"in e?e.ref:""),i=e.name||t;return this._t("marker.toggle_hint_single",{name:i,id:t})},group:e=>this._t("marker.toggle_hint_group",{count:e.length,names:e.map(e=>`${e.name} (${e.entityId})`).join(", ")}),currentNext:(e,i)=>"virtual-light"===e.via?this._t("marker.virtual_light_current",{state:this._t("on"===e.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:t(i)}):this._t("marker.toggle_hint_current",{state:this._toggleStateText(e.entityId,e.state),effect:t(i)}),groupCurrentNext:(e,i)=>this._t("marker.toggle_hint_group_current",{on:e.filter(e=>"on"===e.state).length,count:e.length,effect:t(i)}),skipped:e=>this._t("marker.toggle_hint_skipped",{count:e.length,targets:e.map(e=>{const t=e.entityId||e.ref;return`${e.name||t} (${t}: ${i(e.reason)})`}).join(", ")}),none:e=>this._t(`marker.toggle_none_${e.replaceAll("-","_")}`)})}_effectiveStoredTapAction(e,t){return e.tapActionTouched?e.tapAction:vp(e.originalHasTapAction?e.originalTapAction:null,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._effectiveStoredTapAction(e,t?.primary?.split(".")[0])}_announceToggleDraft(e){const t=this._markerPreviewDevice(e),i=e.tapActionTouched?e:{...e,tapAction:this._effectiveMarkerTapAction(e,t)},s="toggle"===i.tapAction?this._toggleHintLines(this._toggleIntentForDialog(i)).join(" "):"";return{...i,tapHintAnnouncement:s}}_valueBadgeForBinding(e,t){const i={...e,binding:t,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},s=this._markerPreviewDevice(i);if(!s)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const n=[...this._devices.filter(e=>e.id!==s.id),s],o=Wo(this._planHass,s,n),a=jo(this._planHass,s,o);return{valueBadgeEnabled:e.valueBadgeEnabled&&!!a,valueBadgeSource:a,valueBadgeTouched:!0}}_markerSpatialSource(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i={...t,hidden:!1};return po(ho(this._planHass,[...this._devices.filter(e=>e.id!==i.id),i],null,this._virtualLights).filter(e=>e.device.id===i.id))}_markerAutoHasSpatialSource(e){const t={...e,lightRole:"auto",lightRoleTouched:!0},i=this._markerPreviewDevice(t);return!!i&&lo(this._planHass,{...i})}_setMarkerLightRole(e){const t=this._markerDialog;t&&(this._markerDialog={...t,lightRole:e,lightRoleTouched:!0})}_controlRefInfo(e){if(!e.startsWith("marker:"))return{label:this.hass.states[e]?.attributes?.friendly_name||e,sub:e,icon:e.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this._planEntityAvailable(e)};const t=e.slice(7),i=this._markers.find(e=>e.id===t),s=this._devices.find(e=>e.id===t);if(!i||i.removed||!0!==i.is_light)return{label:this._t("marker.control_missing_label"),sub:`${this._t("marker.control_broken")} (${e})`,icon:"mdi:alert-outline",warning:!0};const n=this._serverCfg?.spaces.find(e=>e.id===(s?.space||i.space)),o=!!s&&eo(s).length>0;return{label:i.name||s?.name||t,sub:[n?.title||s?.space||i.space,i.room_id||s?.area,o?"":this._t("marker.control_passive")].filter(Boolean).join(" · "),icon:i.icon||s?.icon||"mdi:lightbulb-outline",warning:!0===i.hidden||!0===s?.hidden}}_valueBadgeCandidateLabel(e){const t=e.source;if("derived_lqi"===t.kind)return this._t("marker.value_badge_lqi");if("derived_marker_state"===t.kind)return this._t("marker.value_badge_marker_state",{name:e.label});if("entity_state"===t.kind)return this._t("marker.value_badge_state",{name:e.label});const i=this.hass.states[t.entity_id]?.attributes?.friendly_name||this._fullRegistryHass.entities[t.entity_id]?.name||t.entity_id;return this._t(`marker.value_badge_attr_${t.attribute}`,{name:i})}_controlCandidates(e){const t=this._markerDraft(e)?.id||e.devId||"",i=[],s=new Set;for(const e of this._markers){if(e.id===t||e.removed||e.hidden||!0!==e.is_light)continue;if("virtual"!==e.binding&&"active"!==this._bindingStatus(e.binding).kind)continue;const n=this._devices.find(t=>t.id===e.id);if(!n||n.hidden)continue;const o=this._controlRefInfo(`marker:${e.id}`);for(const e of eo(n))s.add(e);i.push({value:`marker:${e.id}`,label:o.label,sub:o.sub,icon:o.icon,search:`${o.label} ${o.sub} ${e.id} ${eo(n).join(" ")}`.toLowerCase()})}const n=Object.keys(this.hass.states||{}).filter(t=>Ms(t)&&!s.has(t)&&Xn(e.binding,[t],this._bindingEntities(e.binding)).length>0&&this._planEntityAvailable(t)).map(e=>{const t=this._controlRefInfo(e);return{value:e,label:t.label,sub:e,icon:t.icon,search:`${t.label} ${e}`.toLowerCase()}}),o=e.controlsFilter.trim().toLowerCase();return[...i,...n].filter(t=>!e.controls.includes(t.value)&&(!o||t.search.includes(o))).slice(0,12).map(({search:e,...t})=>t)}_addControlRef(e,t){if(t.startsWith("marker:")){const i=this._markerDraft(e)?.id||e.devId||"";if(!i)return void this._showToast(this._t("toast.marker_binding_required"));const s=t.slice(7),n=this._markerDraft(e);if(function(e,t,i){if(!t||t===i)return!0;const s=new Map;for(const t of e)s.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const n=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;n.has(e)||(n.add(e),o.push(...s.get(e)||[]))}return!1}(n?[...this._markers.filter(e=>e.id!==i),n]:this._markers,i,s))return void this._showToast(this._t("toast.marker_control_cycle"))}this._markerDialog=this._announceToggleDraft({...e,controls:[...e.controls,t],controlsFilter:""})}_setMarkerGlowMode(e){const t=this._markerDialog;if(!t)return;if("auto"===e)return void(this._markerDialog={...t,glowMode:e,glowTouched:!0});const i=this._markerSpatialSource(t),s=xs(i?this._planHass.states[i.eid]:void 0,null,this._fillColors.glow_light.c),n=!t.glowColorDrafted,o="fixed"===e&&!t.glowBrightnessDrafted;this._markerDialog={...t,glowMode:e,glowColor:n?s.c:t.glowColor,glowBrightness:o?Math.max(1,Math.round(100*s.bri)):t.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:t.glowBrightnessDrafted||"fixed"===e,glowTouched:!0}}_renderMarkerDialog(){const e=this._markerDialog,t="virtual"===e.bindingMode,i=this._bindingCandidates();this._bindingEntities(e.binding);const s=t?null:this._bindingStatus(e.binding),n=!t&&this._bindingHasHaPage(e.binding),o=this._markerPreviewDevice(e),a=this._effectiveMarkerTapAction(e,o),r=o?cs(this._serverCfg?.spaces.find(e=>e.id===o.space)):null,l=o?this._markerPreviewDevices(o):this._devices,c="toggle"===a&&o?this._toggleIntent(o,l):null,h=this._toggleHintLines(c),d=o?r_(this._planHass,o,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:r?.showLqi??!1!==this._config?.show_signal,designPreview:!0,activityRuntime:this._activityRt.get(o.id),lightDevices:l,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}):null,p=o?Wo(this._planHass,o,l):[],u=o?jo(this._planHass,o,p):null,_=e.valueBadgeTouched?e.valueBadgeEnabled:!!d?.valueBadge,m=e.valueBadgeTouched?e.valueBadgeSource:d?.valueBadge?.source||e.valueBadgeSource,g=e.valueBadgeTouched?e.valueBadgePosition:d?.valueBadge?.position||e.valueBadgePosition,f=Ao(m),v=!!m&&!p.some(e=>e.key===f),y=p.find(e=>e.key===f),b=d?.valueSource?d.valueSource.attribute?`attr:${d.valueSource.eid}:${d.valueSource.attribute}`:`state:${d.valueSource.eid}`:"",w=this._markerAutoHasSpatialSource(e),k=!!o&&function(e,t){return io(e,t).some(e=>!!e.eid)}(this._planHass,{...o}),x=function(e,t,i,s){const n="always"===e||"auto"===e&&t,o=n&&!i;return{sourceExists:n,fromSourceEnabled:n&&i,manualEnabled:n,radiusEnabled:n,passive:o,effectiveMode:o&&"auto"===s?"fixed":s}}(e.lightRole,w,k,e.glowMode),$=!x.sourceExists,S=!x.fromSourceEnabled,M=x.passive,C=x.effectiveMode,D="never"===e.lightRole?this._t("marker.glow_disabled_never"):"auto"!==e.lightRole||w?M?this._t("marker.glow_passive_hint"):this._t("marker.glow_disabled_no_entity"):this._t("marker.glow_disabled_auto"),P=o?eo(o):[],T=o&&to(o)||"",I=!!e.lightEntity&&!P.includes(e.lightEntity),R=o?up(o):[],A=!!e.toggleEntity&&!R.includes(e.toggleEntity),z="toggle"===a?this._toggleIntentForDialog({...e,toggleEntity:"",toggleEntityTouched:!0}):null,F=z?[...z.targets,...z.skippedTargets].map(e=>e.entityId||("ref"in e?e.ref:"")).filter(Boolean).join(", "):"",E=(()=>{if(t)return null;const s=i.find(t=>t.value===e.binding);if(s)return s.label;const[n,o]=e.binding.split(":");return"device"===n?this._fullRegistryHass.devices[o]?.name_by_user||this._fullRegistryHass.devices[o]?.name||o:this._fullRegistryHass.entities[o]?.name||this.hass.states[o]?.attributes?.friendly_name||o})();return W`this._markerDialog=null}>
@@ -7097,11 +7097,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 -

${this._t(jf[e.display])}

+

${this._t(Uf[e.display])}

${"static_icon"===e.display&&this._bindingHasAlarm(e.binding)?W`
${this._t("marker.static_alarm_warning")} @@ -7203,7 +7203,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922
${e.pdfs.map(e=>W` - ${e.name} + ${e.name} this._removeMarkerPdf(e.url)}>`)} `}
- `}}fv.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_tapConfirm:{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},_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},_rulesDialog:{state:!0},_settingsDialog:{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}},fv.ZOOM_MAX=8,fv.ZOOM_MIN=1/3,fv.styles=[Ke,G_],customElements.get("houseplan-card")||customElements.define("houseplan-card",fv),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${qf} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}vv.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_tapConfirm:{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},_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},_rulesDialog:{state:!0},_settingsDialog:{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}},vv.ZOOM_MAX=8,vv.ZOOM_MIN=1/3,vv.styles=[Ke,V_],customElements.get("houseplan-card")||customElements.define("houseplan-card",vv),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${Wf} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index c01d6c4a..76e7d7e6 100755 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb9222009de6831f5174df777aa8a17d9";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(e,t,s){if(this._$cssResult$=!0,s!==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=s.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&s.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const s=1===e.length?e[0]:t.reduce((t,i,s)=>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[s+1],e[0]);return new n(s,e,i)},a=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new n("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m: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)=>!r(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.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(),s=this.getPropertyDescriptor(e,i,t);void 0!==s&&l(this.prototype,e,s)}}static getPropertyDescriptor(e,t,i){const{get:s,set:n}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:s,set(t){const o=s?.call(this);n?.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=p(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(a(e))}else void 0!==e&&t.push(a(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,s)=>{if(t)i.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of s){const s=document.createElement("style"),n=e.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=t.cssText,i.appendChild(s)}})(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),s=this.constructor._$Eu(e,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(e,t){const i=this.constructor,s=i._$Eh.get(e);if(void 0!==s&&this._$Em!==s){const e=i.getPropertyOptions(s),n="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=s;const o=n.fromAttribute(t,e.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(e,t,i,s=!1,n){if(void 0!==e){const o=this.constructor;if(!1===s&&(n=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(n,t)||i.useDefault&&i.reflect&&n===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:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==n||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===s&&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,s=this[t];!0!==e||this._$AL.has(t)||void 0===s||this.C(t,void 0,i,s)}}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}),(u.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)}$`,D="?"+C,P=`<${D}>`,T=document,I=()=>T.createComment(""),R=e=>null===e||"object"!=typeof e&&"function"!=typeof e,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \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"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(e,t){if(!A(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const J=(e,t)=>{const i=e.length-1,s=[];let n,o=2===t?"":3===t?"":"",a=F;for(let t=0;t"===l[0]?(a=n??F,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,r=l[1],a=void 0===l[3]?O:'"'===l[3]?L:H):a===L||a===H?a=O:a===E||a===N?a=F:(a=O,n=void 0);const d=a===O&&e[t+1].startsWith("/>")?" ":"";o+=a===F?i+P:c>=0?(s.push(r),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?"":"")),s]};class X{constructor({strings:e,_$litType$:t},i){let s;this.parts=[];let n=0,o=0;const a=e.length-1,r=this.parts,[l,c]=J(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(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!==G&&R(this._$AH)?this._$AA.nextSibling.data=e:this.T(T.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,s="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(t);else{const e=new Q(s,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=V.get(e.strings);return void 0===t&&V.set(e.strings,t=new X(e)),t}k(e){A(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,s=0;for(const n of e)s===t.length?t.push(i=new ee(this.O(I()),this.O(I()),this,this.options)):i=t[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(e,t=this,i,s){const n=this.strings;let o=!1;if(void 0===n)e=Z(this,e,t,0),o=!R(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const s=e;let a,r;for(e=n[0],a=0;a{const s=i?.renderBefore??t;let n=s._$litPart$;if(void 0===n){const e=i?.renderBefore??null;s._$litPart$=n=new ee(t.insertBefore(I(),e),e,void 0,i??{})}return n._$AI(e),n},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 pe=2,ue=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const me={},ge=ue(class extends _e{constructor(){super(...arguments),this.ot=me}render(e,t){return t()}update(e,[t,i]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((e,t)=>e===this.ot[t]))return U}else if(this.ot===t)return U;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,i)}}),{I:fe}=ae,ve=e=>e,ye=()=>document.createComment(""),be=(e,t,i)=>{const s=e._$AA.parentNode,n=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=s.insertBefore(ye(),n),o=s.insertBefore(ye(),n);i=new fe(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,a=o!==e;if(a){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==n||a){let e=i._$AA;for(;e!==t;){const t=ve(e).nextSibling;ve(s).insertBefore(e,n),e=t}}}return i},we=(e,t,i=e)=>(e._$AI(t,i),e),ke={},xe=(e,t=ke)=>e._$AH=t,$e=e=>{e._$AR(),e._$AA.remove()},Se=(e,t,i)=>{const s=new Map;for(let n=t;n<=i;n++)s.set(e[n],n);return s},Me=ue(class extends _e{constructor(e){if(super(e),e.type!==pe)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let s;void 0===i?i=t:void 0!==t&&(s=t);const n=[],o=[];let a=0;for(const t of e)n[a]=s?s(t,a):a,o[a]=i(t,a),a++;return{values:o,keys:n}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,s]){const n=(e=>e._$AH)(e),{values:o,keys:a}=this.dt(t,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=we(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=we(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=we(n[d],o[_]),be(e,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=we(n[p],o[u]),be(e,n[d],n[p]),p--,u++;else if(void 0===c&&(c=Se(a,u,_),h=Se(r,d,p)),c.has(r[d]))if(c.has(r[p])){const t=h.get(a[u]),i=void 0!==t?n[t]:null;if(null===i){const t=be(e,n[d]);we(t,o[u]),l[u]=t}else l[u]=we(i,o[u]),be(e,n[d],i),n[t]=null;u++}else $e(n[p]),p--;else $e(n[d]),d++;for(;u<=_;){const t=be(e,l[_+1]);we(t,o[u]),l[u++]=t}for(;d<=p;){const e=n[d++];null!==e&&$e(e)}return this.ut=a,xe(e,l),U}}),Ce=new WeakMap;let De=0;class Pe extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,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-"+ ++De,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?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._useHaDialog)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 s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!e.shiftKey||s!==n&&i.includes(s)?e.shiftKey||s!==o||(e.preventDefault(),n.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Ce.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Ce.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 s=t?Ce.get(t):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),t&&s&&requestAnimationFrame(()=>{const e=Ce.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Ce.delete(t);const s=i?.closest("hp-dialog");i?.isConnected&&s&&e.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){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,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(e)&&t.push(n),n instanceof HTMLSlotElement){for(const e of n.assignedNodes({flatten:!0}))s(e);return}if(n.shadowRoot){for(const e of n.shadowRoot.childNodes)s(e);return}}for(const e of n.childNodes)s(e)}};for(const e of this.childNodes)s(e);const n=this.overlayPortal();return n&&s(n),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})}_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 s={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const e=this._overlays.findIndex(e=>e.token===s.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__="3d7cafaabdfd8cbcf261f9dccec794dd7c424655b825df5e29782f819156ebbf";const e=globalThis,t=e.ShadowRoot&&(void 0===e.ShadyCSS||e.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(e,t,s){if(this._$cssResult$=!0,s!==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=s.get(i)),void 0===e&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),t&&s.set(i,e))}return e}toString(){return this.cssText}};const o=(e,...t)=>{const s=1===e.length?e[0]:t.reduce((t,i,s)=>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[s+1],e[0]);return new n(s,e,i)},a=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new n("string"==typeof e?e:e+"",void 0,i))(t)})(e):e,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(e,t)=>e,v={toAttribute(e,t){switch(t){case Boolean:e=e?m: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)=>!r(e,t),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.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(),s=this.getPropertyDescriptor(e,i,t);void 0!==s&&l(this.prototype,e,s)}}static getPropertyDescriptor(e,t,i){const{get:s,set:n}=c(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:s,set(t){const o=s?.call(this);n?.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=p(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(a(e))}else void 0!==e&&t.push(a(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,s)=>{if(t)i.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const t of s){const s=document.createElement("style"),n=e.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=t.cssText,i.appendChild(s)}})(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),s=this.constructor._$Eu(e,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(t,i.type);this._$Em=e,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(e,t){const i=this.constructor,s=i._$Eh.get(e);if(void 0!==s&&this._$Em!==s){const e=i.getPropertyOptions(s),n="function"==typeof e.converter?{fromAttribute:e.converter}:void 0!==e.converter?.fromAttribute?e.converter:v;this._$Em=s;const o=n.fromAttribute(t,e.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(e,t,i,s=!1,n){if(void 0!==e){const o=this.constructor;if(!1===s&&(n=this[e]),i??=o.getPropertyOptions(e),!((i.hasChanged??y)(n,t)||i.useDefault&&i.reflect&&n===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:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,o??t??this[e]),!0!==n||void 0!==o)||(this._$AL.has(e)||(this.hasUpdated||i||(t=void 0),this._$AL.set(e,t)),!0===s&&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,s=this[t];!0!==e||this._$AL.has(t)||void 0===s||this.C(t,void 0,i,s)}}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}),(u.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)}$`,D="?"+C,P=`<${D}>`,T=document,I=()=>T.createComment(""),R=e=>null===e||"object"!=typeof e&&"function"!=typeof e,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,H=/"/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"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(e,t){if(!A(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(t):t}const J=(e,t)=>{const i=e.length-1,s=[];let n,o=2===t?"":3===t?"":"",a=F;for(let t=0;t"===l[0]?(a=n??F,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,r=l[1],a=void 0===l[3]?O:'"'===l[3]?H:L):a===H||a===L?a=O:a===E||a===N?a=F:(a=O,n=void 0);const d=a===O&&e[t+1].startsWith("/>")?" ":"";o+=a===F?i+P:c>=0?(s.push(r),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?"":"")),s]};class X{constructor({strings:e,_$litType$:t},i){let s;this.parts=[];let n=0,o=0;const a=e.length-1,r=this.parts,[l,c]=J(e,t);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===t||3===t){const e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(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!==G&&R(this._$AH)?this._$AA.nextSibling.data=e:this.T(T.createTextNode(e)),this._$AH=e}$(e){const{values:t,_$litType$:i}=e,s="number"==typeof i?this._$AC(e):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(t);else{const e=new Q(s,this),i=e.u(this.options);e.p(t),this.T(i),this._$AH=e}}_$AC(e){let t=V.get(e.strings);return void 0===t&&V.set(e.strings,t=new X(e)),t}k(e){A(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,s=0;for(const n of e)s===t.length?t.push(i=new ee(this.O(I()),this.O(I()),this,this.options)):i=t[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(e,t=this,i,s){const n=this.strings;let o=!1;if(void 0===n)e=Z(this,e,t,0),o=!R(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const s=e;let a,r;for(e=n[0],a=0;a{const s=i?.renderBefore??t;let n=s._$litPart$;if(void 0===n){const e=i?.renderBefore??null;s._$litPart$=n=new ee(t.insertBefore(I(),e),e,void 0,i??{})}return n._$AI(e),n},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 pe=2,ue=e=>(...t)=>({_$litDirective$:e,values:t});let _e=class{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,i){this._$Ct=e,this._$AM=t,this._$Ci=i}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}};const me={},ge=ue(class extends _e{constructor(){super(...arguments),this.ot=me}render(e,t){return t()}update(e,[t,i]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((e,t)=>e===this.ot[t]))return U}else if(this.ot===t)return U;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,i)}}),{I:fe}=ae,ve=e=>e,ye=()=>document.createComment(""),be=(e,t,i)=>{const s=e._$AA.parentNode,n=void 0===t?e._$AB:t._$AA;if(void 0===i){const t=s.insertBefore(ye(),n),o=s.insertBefore(ye(),n);i=new fe(t,o,e,e.options)}else{const t=i._$AB.nextSibling,o=i._$AM,a=o!==e;if(a){let t;i._$AQ?.(e),i._$AM=e,void 0!==i._$AP&&(t=e._$AU)!==o._$AU&&i._$AP(t)}if(t!==n||a){let e=i._$AA;for(;e!==t;){const t=ve(e).nextSibling;ve(s).insertBefore(e,n),e=t}}}return i},we=(e,t,i=e)=>(e._$AI(t,i),e),ke={},xe=(e,t=ke)=>e._$AH=t,$e=e=>{e._$AR(),e._$AA.remove()},Se=(e,t,i)=>{const s=new Map;for(let n=t;n<=i;n++)s.set(e[n],n);return s},Me=ue(class extends _e{constructor(e){if(super(e),e.type!==pe)throw Error("repeat() can only be used in text expressions")}dt(e,t,i){let s;void 0===i?i=t:void 0!==t&&(s=t);const n=[],o=[];let a=0;for(const t of e)n[a]=s?s(t,a):a,o[a]=i(t,a),a++;return{values:o,keys:n}}render(e,t,i){return this.dt(e,t,i).values}update(e,[t,i,s]){const n=(e=>e._$AH)(e),{values:o,keys:a}=this.dt(t,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=we(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=we(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=we(n[d],o[_]),be(e,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=we(n[p],o[u]),be(e,n[d],n[p]),p--,u++;else if(void 0===c&&(c=Se(a,u,_),h=Se(r,d,p)),c.has(r[d]))if(c.has(r[p])){const t=h.get(a[u]),i=void 0!==t?n[t]:null;if(null===i){const t=be(e,n[d]);we(t,o[u]),l[u]=t}else l[u]=we(i,o[u]),be(e,n[d],i),n[t]=null;u++}else $e(n[p]),p--;else $e(n[d]),d++;for(;u<=_;){const t=be(e,l[_+1]);we(t,o[u]),l[u++]=t}for(;d<=p;){const e=n[d++];null!==e&&$e(e)}return this.ut=a,xe(e,l),U}}),Ce=new WeakMap;let De=0;class Pe extends he{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,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-"+ ++De,this._focusInitial=()=>{const e=this._focusableElements(),t=e.find(e=>e.hasAttribute("autofocus"))||e[0]||(this._useHaDialog?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._useHaDialog)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 s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!e.shiftKey||s!==n&&i.includes(s)?e.shiftKey||s!==o||(e.preventDefault(),n.focus()):(e.preventDefault(),o.focus())},this._onFallbackCancel=e=>{e.preventDefault(),this._requestClose()},this._onFallbackClick=e=>{this.dismissOnScrim&&e.target===e.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const e=this.getRootNode();this._focusRoot=e;const t=Ce.get(e)||{dialogs:new Set,opener:this._opener};t.dialogs.add(this),Ce.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 s=t?Ce.get(t):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),t&&s&&requestAnimationFrame(()=>{const e=Ce.get(t);if(!e)return;if(!e.dialogs.size)return e.opener?.isConnected&&e.opener.focus({preventScroll:!0}),void Ce.delete(t);const s=i?.closest("hp-dialog");i?.isConnected&&s&&e.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(e){if(super.firstUpdated(e),!this._useHaDialog){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,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(e)&&t.push(n),n instanceof HTMLSlotElement){for(const e of n.assignedNodes({flatten:!0}))s(e);return}if(n.shadowRoot){for(const e of n.shadowRoot.childNodes)s(e);return}}for(const e of n.childNodes)s(e)}};for(const e of this.childNodes)s(e);const n=this.overlayPortal();return n&&s(n),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})}_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 s={...e,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const e=this._overlays.findIndex(e=>e.token===s.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``:G} ${this.title} `;return this._useHaDialog?W`"number"==typeof e&&Number.isFinite(e)))return null;const[i,s,n]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${s}, ${n})`}const Fe=(e,t,i)=>Math.min(i,Math.max(t,Number.isFinite(e)?e:t));function Ee(e){return Number.isFinite(e)?(e%360+360)%360:0}function Ne(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^#/,""),i=/^[0-9a-fA-F]{3}$/.test(t)?t.split("").map(e=>`${e}${e}`).join(""):t;return/^[0-9a-fA-F]{6}$/.test(i)?`#${i.toLowerCase()}`:null}function Oe(e){const t=Ne(e);return t?{r:Number.parseInt(t.slice(1,3),16),g:Number.parseInt(t.slice(3,5),16),b:Number.parseInt(t.slice(5,7),16)}:null}function He(e){const t=Fe(e.r,0,255)/255,i=Fe(e.g,0,255)/255,s=Fe(e.b,0,255)/255,n=Math.max(t,i,s),o=n-Math.min(t,i,s);let a=0;return o>0&&(a=n===t?(i-s)/o%6*60:n===i?60*((s-t)/o+2):60*((t-i)/o+4)),{h:Ee(a),s:0===n?0:o/n*100,v:100*n}}function Le(e){return function(e){const t=e=>Math.round(Fe(e,0,255)).toString(16).padStart(2,"0");return`#${t(e.r)}${t(e.g)}${t(e.b)}`}(function(e){const t=Ee(e.h),i=Fe(e.s,0,100)/100,s=Fe(e.v,0,100)/100,n=s*i,o=n*(1-Math.abs(t/60%2-1)),a=s-n;let r;return r=t<60?[n,o,0]:t<120?[o,n,0]:t<180?[0,n,o]:t<240?[0,o,n]:t<300?[o,0,n]:[n,0,o],{r:Math.round(255*(r[0]+a)),g:Math.round(255*(r[1]+a)),b:Math.round(255*(r[2]+a))}}(e))}function Be(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 qe(e,t,i,s=7,n=8){const o=i.left+n,a=i.top+n,r=Math.max(o,i.left+i.width-n),l=Math.max(a,i.top+i.height-n),c=Math.max(0,r-o),h=Math.max(0,l-a),d=Math.min(Math.max(0,t.width),c),p=Math.min(Math.max(0,t.height),h);let u=e.left;u+d>r&&(u=e.right-d),u=Math.min(Math.max(o,u),Math.max(o,r-d));const _=e.bottom+s,m=e.top-s-p;let g="bottom",f=_;return _+p>l&&m>=a?(g="top",f=Math.min(Math.max(a,m),Math.max(a,l-p))):f=Math.min(Math.max(a,_),Math.max(a,l-p)),{left:Math.round(u),top:Math.round(f),side:g,maxWidth:Math.round(c),maxHeight:Math.round(h)}}class We{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(e,t){const i=this.ensureFallback();return i?(le(W`${e}`,i),i):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&le(G,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}}const je={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ue extends he{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=je,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=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closePicker(!1,"outside")},this._keyDown=e=>{this._open&&"Escape"===e.key&&(e.preventDefault(),e.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new We(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const e=this._window();e&&(this._pickerRaf=e.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 e=this.ownerDocument.defaultView;e?.addEventListener("resize",this._queuePickerPosition),e?.addEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.addEventListener("resize",this._queuePickerPosition),e?.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 e=this.ownerDocument.defaultView;e?.removeEventListener("resize",this._queuePickerPosition),e?.removeEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),e?.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(e){if(e.has("color")){const e=Ne(this.color);e&&e!==this._lastValidColor&&this._syncFromColor(e,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(e.has("color")||e.has("opacity")||e.has("showOpacity")||e.has("label")||e.has("opacityLabel")||e.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:e=>this._closePicker("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closePicker())))}_closePicker(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".picker");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const e=Ue.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),e)}_positionPicker(){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 s=Be(e);if(i.style.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._supportsPopover()&&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.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden"}const n=t.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=qe(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}_labels(){const e=this.pickerLabels||je;return{title:e.title||je.title,hue:e.hue||je.hue,saturation:e.saturation||je.saturation,value:e.value||je.value,hex:e.hex||je.hex,invalidHex:e.invalidHex||je.invalidHex}}_syncFromColor(e,t){const i=Ne(Ie(e,"#607d8b"))||"#607d8b",s=Oe(i);if(!s)return;const n=He(s);(!t||n.s>1e-4)&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._hexDraft=i,this._hexInvalid=!1,this._lastValidColor=i}_cancelQueuedEmit(e=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,e&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const e=this._window();e?this._emitRaf=e.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(Le({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(e){this._hue=Math.min(359,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setSaturation(e){this._saturation=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setValue(e){this._value=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setOpacity(e){this._emit(this._lastValidColor,e)}_shiftRangeKey(e,t,i,s,n){if(!e.shiftKey)return;const o="ArrowRight"===e.key||"ArrowUp"===e.key?10:"ArrowLeft"===e.key||"ArrowDown"===e.key?-10:0;o&&(e.preventDefault(),e.stopPropagation(),n(Math.min(s,Math.max(i,t+o))))}_setSvFromPointer(e,t){const i=e.currentTarget.getBoundingClientRect();i.width&&i.height&&(this._saturation=Math.min(100,Math.max(0,(e.clientX-i.left)/i.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(e.clientY-i.top)/i.height))),t?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(e){if(e.preventDefault(),e.stopPropagation(),null===this._activePointerId){this._activePointerId=e.pointerId;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}this._setSvFromPointer(e,!0)}}_svPointerMove(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!1))}_svPointerUp(e){if(e.pointerId===this._activePointerId){e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!0),this._activePointerId=null;try{e.currentTarget.releasePointerCapture(e.pointerId)}catch{}}}_svPointerCancel(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(e){e.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(e){const t=e.target.value;this._hexDraft=t,this._hexInvalid=!1;const i=Ne(t),s=i?Oe(i):null;if(!i||!s)return;const n=He(s);n.s>1e-4&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._emit(i,this.opacity,!0)}_commitHex(){const e=Ne(this._hexDraft);if(!e)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=e,this._hexInvalid=!1,e!==this._lastValidColor&&this._emit(e,this.opacity)}_hexKeyDown(e){"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),this._commitHex())}_emit(e,t,i=!1){const s=Ne(e)||this._lastValidColor,n=Number.isFinite(t)?t:Number(this.opacity)||0,o=Math.min(1,Math.max(0,n));this.color=s,this.opacity=o,this._lastValidColor=s,i||(this._hexDraft=s),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:s,opacity:o},bubbles:!0,composed:!0}))}_pickerTemplate(e){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),i=Le({h:this._hue,s:this._saturation,v:this._value}),s=Le({h:this._hue,s:100,v:100}),n=this._labels(),o=Math.round(this._hue),a=Math.round(this._saturation),r=Math.round(this._value);return W` + `,customElements.get("hp-dialog")||customElements.define("hp-dialog",Pe);const Te=/^#[0-9a-fA-F]{6}$/;function Ie(e,t){return"string"==typeof e&&Te.test(e)?e:t}const Re="(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])",Ae=new RegExp(`^rgb\\(${Re}, ${Re}, ${Re}\\)$`);function ze(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,s,n]=t.map(e=>Math.round(Math.min(255,Math.max(0,e))));return`rgb(${i}, ${s}, ${n})`}const Fe=(e,t,i)=>Math.min(i,Math.max(t,Number.isFinite(e)?e:t));function Ee(e){return Number.isFinite(e)?(e%360+360)%360:0}function Ne(e){if("string"!=typeof e)return null;const t=e.trim().replace(/^#/,""),i=/^[0-9a-fA-F]{3}$/.test(t)?t.split("").map(e=>`${e}${e}`).join(""):t;return/^[0-9a-fA-F]{6}$/.test(i)?`#${i.toLowerCase()}`:null}function Oe(e){const t=Ne(e);return t?{r:Number.parseInt(t.slice(1,3),16),g:Number.parseInt(t.slice(3,5),16),b:Number.parseInt(t.slice(5,7),16)}:null}function Le(e){const t=Fe(e.r,0,255)/255,i=Fe(e.g,0,255)/255,s=Fe(e.b,0,255)/255,n=Math.max(t,i,s),o=n-Math.min(t,i,s);let a=0;return o>0&&(a=n===t?(i-s)/o%6*60:n===i?60*((s-t)/o+2):60*((t-i)/o+4)),{h:Ee(a),s:0===n?0:o/n*100,v:100*n}}function He(e){return function(e){const t=e=>Math.round(Fe(e,0,255)).toString(16).padStart(2,"0");return`#${t(e.r)}${t(e.g)}${t(e.b)}`}(function(e){const t=Ee(e.h),i=Fe(e.s,0,100)/100,s=Fe(e.v,0,100)/100,n=s*i,o=n*(1-Math.abs(t/60%2-1)),a=s-n;let r;return r=t<60?[n,o,0]:t<120?[o,n,0]:t<180?[0,n,o]:t<240?[0,o,n]:t<300?[o,0,n]:[n,0,o],{r:Math.round(255*(r[0]+a)),g:Math.round(255*(r[1]+a)),b:Math.round(255*(r[2]+a))}}(e))}function Be(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 qe(e,t,i,s=7,n=8){const o=i.left+n,a=i.top+n,r=Math.max(o,i.left+i.width-n),l=Math.max(a,i.top+i.height-n),c=Math.max(0,r-o),h=Math.max(0,l-a),d=Math.min(Math.max(0,t.width),c),p=Math.min(Math.max(0,t.height),h);let u=e.left;u+d>r&&(u=e.right-d),u=Math.min(Math.max(o,u),Math.max(o,r-d));const _=e.bottom+s,m=e.top-s-p;let g="bottom",f=_;return _+p>l&&m>=a?(g="top",f=Math.min(Math.max(a,m),Math.max(a,l-p))):f=Math.min(Math.max(a,_),Math.max(a,l-p)),{left:Math.round(u),top:Math.round(f),side:g,maxWidth:Math.round(c),maxHeight:Math.round(h)}}class We{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(e,t){const i=this.ensureFallback();return i?(le(W`${e}`,i),i):null}surface(e,t){return t?this.owner.shadowRoot?.querySelector(e)||null:this.root?.querySelector(e)||null}destroy(){this.root&&le(G,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}}const je={title:"Color picker",hue:"Hue",saturation:"Saturation",value:"Brightness",hex:"Hex color",invalidHex:"Enter a 3- or 6-digit hex color"};class Ue extends he{constructor(){super(...arguments),this.label="",this.opacityLabel="Opacity",this.color="#607d8b",this.opacity=1,this.disabled=!1,this.showOpacity=!0,this.pickerLabels=je,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=e=>{if(!this._open)return;const t=e.composedPath();this._floating.containsPath(t)||this._closePicker(!1,"outside")},this._keyDown=e=>{this._open&&"Escape"===e.key&&(e.preventDefault(),e.stopImmediatePropagation(),this._closePicker(this._floating.ownsActiveElement(),"escape"))},this._floating=new We(this,"color-opacity",this._keyDown),this._queuePickerPosition=()=>{if(!this._open)return;this._pickerRaf&&cancelAnimationFrame(this._pickerRaf);const e=this._window();e&&(this._pickerRaf=e.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 e=this.ownerDocument.defaultView;e?.addEventListener("resize",this._queuePickerPosition),e?.addEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.addEventListener("resize",this._queuePickerPosition),e?.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 e=this.ownerDocument.defaultView;e?.removeEventListener("resize",this._queuePickerPosition),e?.removeEventListener("orientationchange",this._queuePickerPosition),e?.visualViewport?.removeEventListener("resize",this._queuePickerPosition),e?.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(e){if(e.has("color")){const e=Ne(this.color);e&&e!==this._lastValidColor&&this._syncFromColor(e,this._open)}this.disabled&&this._open?this._closePicker():this._open&&(this._supportsPopover()||this._renderFallback(),(e.has("color")||e.has("opacity")||e.has("showOpacity")||e.has("label")||e.has("opacityLabel")||e.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:e=>this._closePicker("escape"===e&&this._floating.ownsActiveElement(),e)})||null:this._closePicker())))}_closePicker(e=!1,t="exclusive"){if(!this._open&&!this._floating.hasFallback)return;const i=this._overlayDispose;this._overlayDispose=null,i?.();const s=this.renderRoot.querySelector(".picker");if(s?.hidePopover)try{s.matches(":popover-open")&&s.hidePopover()}catch{}this._open=!1,this._cancelQueuedEmit(),this._activePointerId=null,this._floating.destroy(),e&&this.updateComplete.then(()=>this.renderRoot.querySelector(".trigger")?.focus())}_surface(){return this._floating.surface(".picker",this._supportsPopover())}_renderFallback(){const e=Ue.styles.cssText;this._floating.renderFallback(this._pickerTemplate(!1),e)}_positionPicker(){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 s=Be(e);if(i.style.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden",this._supportsPopover()&&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.maxWidth=`${Math.max(0,s.width-16)}px`,i.style.maxHeight=`${Math.max(0,s.height-16)}px`,i.style.visibility="hidden"}const n=t.getBoundingClientRect(),o=i.getBoundingClientRect();if(!(n.width&&n.height&&o.width&&o.height))return!1;const a=qe(n,o,s);return i.style.left=`${a.left}px`,i.style.top=`${a.top}px`,i.dataset.side=a.side,i.style.visibility="",!0}_labels(){const e=this.pickerLabels||je;return{title:e.title||je.title,hue:e.hue||je.hue,saturation:e.saturation||je.saturation,value:e.value||je.value,hex:e.hex||je.hex,invalidHex:e.invalidHex||je.invalidHex}}_syncFromColor(e,t){const i=Ne(Ie(e,"#607d8b"))||"#607d8b",s=Oe(i);if(!s)return;const n=Le(s);(!t||n.s>1e-4)&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._hexDraft=i,this._hexInvalid=!1,this._lastValidColor=i}_cancelQueuedEmit(e=!1){this._emitRaf&&cancelAnimationFrame(this._emitRaf),this._emitRaf=0,e&&this._syncFromColor(this._lastValidColor,!0)}_queueHsvEmit(){if(this._emitRaf)return;const e=this._window();e?this._emitRaf=e.requestAnimationFrame(()=>{this._emitRaf=0,this._emitHsv()}):this._emitHsv()}_emitHsv(){this._emit(He({h:this._hue,s:this._saturation,v:this._value}),this.opacity)}_setHue(e){this._hue=Math.min(359,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setSaturation(e){this._saturation=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setValue(e){this._value=Math.min(100,Math.max(0,Number.isFinite(e)?e:0)),this._emitHsv()}_setOpacity(e){this._emit(this._lastValidColor,e)}_shiftRangeKey(e,t,i,s,n){if(!e.shiftKey)return;const o="ArrowRight"===e.key||"ArrowUp"===e.key?10:"ArrowLeft"===e.key||"ArrowDown"===e.key?-10:0;o&&(e.preventDefault(),e.stopPropagation(),n(Math.min(s,Math.max(i,t+o))))}_setSvFromPointer(e,t){const i=e.currentTarget.getBoundingClientRect();i.width&&i.height&&(this._saturation=Math.min(100,Math.max(0,(e.clientX-i.left)/i.width*100)),this._value=Math.min(100,Math.max(0,100*(1-(e.clientY-i.top)/i.height))),t?(this._cancelQueuedEmit(),this._emitHsv()):this._queueHsvEmit())}_svPointerDown(e){if(e.preventDefault(),e.stopPropagation(),null===this._activePointerId){this._activePointerId=e.pointerId;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}this._setSvFromPointer(e,!0)}}_svPointerMove(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!1))}_svPointerUp(e){if(e.pointerId===this._activePointerId){e.preventDefault(),e.stopPropagation(),this._setSvFromPointer(e,!0),this._activePointerId=null;try{e.currentTarget.releasePointerCapture(e.pointerId)}catch{}}}_svPointerCancel(e){e.pointerId===this._activePointerId&&(e.preventDefault(),e.stopPropagation(),this._activePointerId=null,this._cancelQueuedEmit(!0))}_svLostPointerCapture(e){e.pointerId===this._activePointerId&&(this._activePointerId=null,this._cancelQueuedEmit(!0))}_hexInput(e){const t=e.target.value;this._hexDraft=t,this._hexInvalid=!1;const i=Ne(t),s=i?Oe(i):null;if(!i||!s)return;const n=Le(s);n.s>1e-4&&(this._hue=n.h),this._saturation=n.s,this._value=n.v,this._emit(i,this.opacity,!0)}_commitHex(){const e=Ne(this._hexDraft);if(!e)return this._hexDraft=this._lastValidColor,void(this._hexInvalid=!0);this._hexDraft=e,this._hexInvalid=!1,e!==this._lastValidColor&&this._emit(e,this.opacity)}_hexKeyDown(e){"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),this._commitHex())}_emit(e,t,i=!1){const s=Ne(e)||this._lastValidColor,n=Number.isFinite(t)?t:Number(this.opacity)||0,o=Math.min(1,Math.max(0,n));this.color=s,this.opacity=o,this._lastValidColor=s,i||(this._hexDraft=s),this._hexInvalid=!1,this.dispatchEvent(new CustomEvent("hp-color-opacity-change",{detail:{color:s,opacity:o},bubbles:!0,composed:!0}))}_pickerTemplate(e){const t=Math.round(100*Math.min(1,Math.max(0,Number(this.opacity)||0))),i=He({h:this._hue,s:this._saturation,v:this._value}),s=He({h:this._hue,s:100,v:100}),n=this._labels(),o=Math.round(this._hue),a=Math.round(this._saturation),r=Math.round(this._value);return W` `}houseplanContinuityTrace(){return[...this._continuityHistory,...this._continuity.trace].slice(-80).map(e=>({...e,...e.stage?{stage:[...e.stage]}:{}}))}render(){if(!this._config)return G;const e=this._snap?.config;if(!e)return this._errorCard(la(this._lang,"space_card.loading"));const t=this._config.space,i=this._renderDeviceSnapshot,s=C_({hass:i?.hass||this.hass,cfg:e,layout:this._snap?.layout||{},spaceId:t,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:e=>this._signer.display(this.hass,e),assetLoaded:this._onAssetLoaded,registry:i?{revision:i.sourceSequence,authoritative:!1,access:"limited",entities:{...i.hass.entities},devices:{...i.hass.devices},lastSuccess:i.capturedAt}:An(this.hass),devices:[...i?.devices||this._devices],presentations:i?.presentations,activityRuntime:this._activityRuntime,reducedMotion:this._reducedMotion,virtualLights:this._snap?.virtualLights,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(la(this._lang,"space_card.not_found",{id:t}));const n=Th(e).find(e=>e.id===t),o=void 0!==this._config.title?this._config.title:n?.title||"",a=!1!==this._config.show_button,r=this._config.button_label||la(this._lang,"space_card.button"),l=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null;return W` ${r}
`:G} - `}}U_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},U_.styles=[Ke,o` + `}}G_.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0},_continuityEpoch:{state:!0}},G_.styles=[Ke,o` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -4757,7 +4757,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",U_),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 G_=o` + `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",G_),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 V_=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); @@ -4928,7 +4928,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 .editor-secondary { transition: none; } .editor-group-launcher .group-chevron { transition: none; } } -`,V_=(e,t)=>(e.composedPath?.()||[e.target]).some(e=>(e=>!!e&&"function"==typeof e.matches)(e)&&t(e));class K_{constructor(e){this.host=e,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=e=>{this.handleOutsideDismiss(e)}}get hasOpenGroup(){return null!==this._openGroupId}get groupGeneration(){return this._groupGeneration}activeGroup(e){return this._openGroupId&&e.find(e=>e.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 e="palette"===this._currentModel?.kind?this._currentModel.dismiss:void 0,t=!!this._openGroupId||!!e;this._openGroupId=null,e?.(),e&&(this._currentModel=null),this._clearDismissClick(),t&&this.host.requestUpdate(),this._syncGlobalDismissListener()}openPalette(){this._openGroupId&&this.closeGroup(!1)}toggleGroup(e,t){this._openGroupId!==t?e.some(e=>e.id===t)&&("palette"===this._currentModel?.kind&&this._currentModel.dismiss?.(),this._groupGeneration+=1,this._openGroupId=t,this.host.clearTip(),this.host.requestUpdate(),this._syncGlobalDismissListener()):this.closeGroup(!1)}closeGroup(e){const t=this._openGroupId;this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener(),e&&t&&this.host.updateComplete().then(()=>{const e=this.host.root(),i=[...e.querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===t&&null!==e.offsetParent);(i||e.querySelector(".editbar-tools"))?.focus?.()})}renderGroupLauncher(e,t,i){const s=this._openGroupId===e.id,n=e.items.find(t=>t.id===e.activeItemId&&!t.disabled);return W`
- `}afterRender(){const e=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const t=e.querySelector(".editor-secondary"),i=this._renderedContext,s=t?.dataset.contextId||"";if(s&&i&&s!==i&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=t?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=s,i&&s!==i&&this._focusOwned){const i=s?t?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):e.querySelector(".editbar-tools");i?.focus?.(),this._focusOwned=!!s}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const t=[...e.querySelectorAll("[data-editor-palette]")].find(e=>e.dataset.editorPalette===this._currentModel?.launcherId);t&&null!==t.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const n=[...e.querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===this._openGroupId);n&&null!==n.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(e){if(V_(e,e=>e.hasAttribute("data-editor-navigation")))return"pointerdown"===e.type?this.closeForNavigation():"click"===e.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===e.type&&this._dismissPointerTarget){return!!(e.composedPath?.()||[e.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),e.preventDefault(),e.stopImmediatePropagation(),!0)}const t=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===e.type&&(this._openGroupId||t)){const i=V_(e,e=>e.classList.contains("editor-secondary")),s=V_(e,e=>!!e.dataset.editorGroup),n=V_(e,e=>!!e.dataset.editorPalette),o=V_(e,e=>e.classList.contains("stage"));if(!(i||s||n||"stay-open-on-canvas"===t?.dismissPolicy&&o))return e.preventDefault(),e.stopImmediatePropagation(),this._dismissPointerTarget=e.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):t?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(e,t,i){if(e.id!==this._openGroupId)return;const s=e.items.find(e=>e.id===t);if(!s||s.disabled)return;const n="toggle"===s.role&&"stay-open"===s.closePolicy,o=0===i.detail;n||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),s.invoke(),!n&&o&&this.host.updateComplete().then(()=>{const t=[...this.host.root().querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e.id&&null!==t.offsetParent);t?.focus()})}_groupKeydown(e){if(!["ArrowLeft","ArrowRight","Home","End"].includes(e.key))return;const t=[...e.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!t.length)return;e.preventDefault();const i=t.indexOf(this.host.root().activeElement),s="Home"===e.key?0:"End"===e.key?t.length-1:"ArrowLeft"===e.key?i<=0?t.length-1:i-1:(i+1)%t.length;t[s]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const e=!this._blocked&&"palette"===this._currentModel?.kind,t=!!this._openGroupId||e||!!this._dismissPointerTarget;t!==this._globalDismissListening&&(this._globalDismissListening=t,t?(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 Y_=e=>e.stopPropagation(),J_=4.166666666666667e-9;function X_(e){return Number.isFinite(e)?Math.round(e/Fh)*Fh:e}const Z_=(e,t,i,s)=>Math.hypot(i-e,s-t),Q_=(e,t,i,s,n,o,a,r)=>Math.hypot(Math.max(Math.abs(n-e),Math.abs(n+a-(e+i))),Math.max(Math.abs(o-t),Math.abs(o+r-(t+s)))),em=(e,t,i,s,n,o,a)=>{const r=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*r,h=Math.sin(i*l)*r,d=Math.cos(a*l)*r,p=Math.sin(a*l)*r,u=Math.max(Z_(e+c,t+h,n+d,o+p),Z_(e-c,t-h,n-d,o-p)),_=Math.max(Z_(e+c,t+h,n-d,o-p),Z_(e-c,t-h,n+d,o+p));return Math.min(u,_)},tm=e=>{const t=Number(e?.cell_cm);return t>0?t:5};function im(e,t){const i=JSON.parse(JSON.stringify(e||[])),s=JSON.parse(JSON.stringify(t||{}));let n=0,o=0,a=0,r=0,l="",c=0,h=0,d=0;const p=(e,t)=>{Number.isFinite(e)&&Number.isFinite(t)&&e!==t&&Math.abs(t-e)<=J_&&h++},u=(e,t)=>{p(e[0],t[0]),p(e[1],t[1])},_={};let m=5;for(const e of i){const t=tm(e);null!=e?.id&&(_[String(e.id)]=t),t>m&&(m=t)}const g=(e,t,i,s=!1)=>{if(!(e>J_||s))return;n++,e>a&&(a=e);const o=e*Ah*t;o>r&&(r=o,l=i)};for(const e of i){const t=tm(e),i=null!=e?.id?String(e.id):"";for(const s of e.rooms||[]){o++;let e=0;if(s.poly?.length)s.poly=s.poly.map(t=>{const i=[X_(t[0]),X_(t[1])];return e=Math.max(e,Z_(t[0],t[1],i[0],i[1])),u(t,i),i});else if(null!=s.x&&null!=s.y){const t=s.x,i=s.y,n=s.w||0,o=s.h||0,a=X_(t+n),r=X_(i+o),l=X_(t),c=X_(i),h=Math.max(Fh,a-l),d=Math.max(Fh,r-c);e=Q_(t,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d,p(t,l),p(i,c),p(t+n,l+h),p(i+o,c+d)}g(e,t,i)}for(const s of e.room_drafts||[]){o++;let e=0;const n=s.points||[],a=n.map(t=>{const i=[X_(t[0]),X_(t[1])];return e=Math.max(e,Z_(t[0],t[1],i[0],i[1])),i}),r=a.length?[a[0]]:[],l=a.length?[[n[0],a[0]]]:[],c=[];for(let e=0;e+1=2)for(const[e,t]of l)u(e,t);g(e,t,i)}if(Array.isArray(e.room_drafts)){const t=e.room_drafts.length;e.room_drafts=e.room_drafts.filter(e=>e.points?.length>=2),d+=t-e.room_drafts.length,e.room_drafts.length||delete e.room_drafts}for(const s of e.partitions||[]){o++;const n=[s.a[0],s.a[1]],a=[s.b[0],s.b[1]],r=[X_(n[0]),X_(n[1])],l=[X_(a[0]),X_(a[1])];let c=Math.max(Z_(n[0],n[1],r[0],r[1]),Z_(a[0],a[1],l[0],l[1]));const h=Z_(r[0],r[1],l[0],l[1]),d=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===s.id).every(e=>{const t=Number(e.length),i=Number(e.host.t),s=i*h;return Number.isFinite(t)&&t>0&&Number.isFinite(i)&&i>=0&&i<=1&&s-t/2>=-J_&&s+t/2<=h+J_});h>J_&&d?(s.a=r,s.b=l,u(n,r),u(a,l)):c=0,g(c,t,i)}for(const s of e.wall_columns||[]){o++;const e=[s.center[0],s.center[1]],n=[X_(e[0]),X_(e[1])],a=Z_(e[0],e[1],n[0],n[1]);s.center=n,u(e,n),g(a,t,i)}for(const s of e.decor||[]){o++;let e=0;if("line"===s.kind){const t=[s.x1,s.y1],i=[s.x2,s.y2],n=[X_(t[0]),X_(t[1])],o=[X_(i[0]),X_(i[1])];e=Math.max(Z_(t[0],t[1],n[0],n[1]),Z_(i[0],i[1],o[0],o[1])),s.x1=n[0],s.y1=n[1],s.x2=o[0],s.y2=o[1],u(t,n),u(i,o)}else{const t=s.x,i=s.y,n=s.w,o=s.h,a=X_(t),r=X_(i);if(null!=s.w&&null!=s.h){const l=X_(t+n),c=X_(i+o),h=Math.max(Fh,l-a),d=Math.max(Fh,c-r);e=Q_(t,i,n,o,a,r,h,d),s.w=h,s.h=d,p(t+n,a+h),p(i+o,r+d)}else e=Z_(t,i,a,r);s.x=a,s.y=r,p(t,a),p(i,r)}g(e,t,i)}for(const s of e.openings||[]){if(o++,"partition"===s.host?.kind){const n=(e.partitions||[]).find(e=>e.id===s.host.id),o=Number(s.host.t);if(!n||!Number.isFinite(o)||o<0||o>1)continue;const a=n.b[0]-n.a[0],r=n.b[1]-n.a[1];if(Math.hypot(a,r)<=J_)continue;const l=n.a[0]+a*o,h=n.a[1]+r*o;let d=180*Math.atan2(r,a)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(s.angle),u=!(Number.isFinite(p)&&p===d),_=em(s.x,s.y,Number.isFinite(p)?p:d,Number(s.length)||0,l,h,d);s.x=l,s.y=h,s.angle=d,u&&c++,g(_,t,i,u);continue}const n=ui([s.x,s.y],e.rooms||[],.025,{step:Fh,length:Number(s.length)||0});if(!n)continue;const a=Number(s.angle),r=!(Number.isFinite(a)&&a===n.angle),l=em(s.x,s.y,Number.isFinite(a)?a:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,r&&c++,g(l,t,i,r)}}for(const[e,t]of Object.entries(s)){if(!t||"object"!=typeof t)continue;const i=t;if("number"!=typeof i.x||"number"!=typeof i.y)continue;o++;const n=X_(i.x),a=X_(i.y),r=Z_(i.x,i.y,n,a);s[e]={...i,x:n,y:a},p(i.x,n),p(i.y,a);const l="string"==typeof i.s?i.s:"";g(r,_[l]??m,l)}return{spaces:i,layout:s,report:{moved:n,coordsCanonicalized:h,total:o,maxShift:a,maxShiftCm:r,maxSpace:l,rotated:c,removedDrafts:d},changed:n>0||h>0}}function sm(e,t){const i=t.base+e*t.span;return Math.min(1,Math.max(0,i))}const nm=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),om=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1]);function am(e,t,i){const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;if(o<=0)return om(e,t);let a=((e[0]-t[0])*s+(e[1]-t[1])*n)/o;return a=Math.max(0,Math.min(1,a)),Math.hypot(e[0]-(t[0]+s*a),e[1]-(t[1]+n*a))}function rm(e,t,i,s,n){for(const s of t)if(s&&!i.has(s.id)&&nm(s.a)&&nm(s.b)&&(om(e,s.a)<=n||om(e,s.b)<=n))return!0;for(const t of s?.roomPolygons||[])for(let i=0;i0&&r>0))return null;if(Math.abs(n[0]/a*(o[1]/r)-n[1]/a*(o[0]/r))>i)return null;for(const[i,n]of[[e.a,t.a],[e.a,t.b],[e.b,t.a],[e.b,t.b]])if(om(i,n)<=s)return[(i[0]+n[0])/2,(i[1]+n[1])/2];return null}function cm(e,t){const i=.05*(Number(t.pitch)>0?Number(t.pitch):1),s=new Map;let n=(e||[]).filter(e=>!!e&&"string"==typeof e.id&&nm(e.a)&&nm(e.b));if(n.length<2)return{partitions:[...e||[]],merged:0,openingMoves:[]};const o=t.seedIds&&t.seedIds.length?new Set(t.seedIds):null;let a=0;for(let e=0;eom(t,e.at)>i);let h=c[0]??r.a,d=c[c.length-1]??l.b;if(c.length<2&&(h=r.a,d=l.b),d[0]0?(d[0]-h[0])/u:0,m=u>0?(d[1]-h[1])/u:0,g=e=>u>0?((e[0]-h[0])*_+(e[1]-h[1])*m)/u:0;for(const e of[r,l]){const t=g(e.a),i=g(e.b)-t;for(const[n,o]of s)o.toId===e.id&&s.set(n,{fromId:o.fromId,toId:p.id,base:t+o.base*i,span:o.span*i});s.set(e.id,{fromId:e.id,toId:p.id,base:t,span:i})}o&&o.add(p.id),n=n.filter((t,i)=>i!==e.i&&i!==e.j),n.splice(e.i,0,p),a++}return{partitions:n,merged:a,openingMoves:[...s.values()]}}function hm(e,t,i,s){if(!e?.length||!i.length)return 0;const n=new Map(i.map(e=>[e.fromId,e]));let o=0;for(const i of e){const e=i?.host;if(!e||"partition"!==e.kind)continue;const a=n.get(e.id);if(!a)continue;i.host={...e,id:a.toId,t:sm(e.t,a)},o++;const r=Tp(i,t,s.coordScale,s.cellCm,s.gridPitch).resolved;r&&Object.assign(i,Np(i,r,s.coordScale))}return o}function dm(e,t){const i=t?.excludeDraftId||null;return{roomPolygons:(e?.rooms||[]).map(e=>hi(e)).filter(e=>!!e),columns:e?.wall_columns||[],draftEnds:(e?.room_drafts||[]).flatMap(e=>{if(i&&e?.id===i)return[];const t=e?.points||[];return t.length?[t[0],t[t.length-1]]:[]})}}const pm=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),um=(e,t)=>e[0]-t[0]||e[1]-t[1],_m=(e,t,i,s)=>[e[0]+t*s,e[1]+i*s],mm=(e,t,i,s)=>(e[0]-t[0])*i+(e[1]-t[1])*s,gm=(e,t,i,s,n)=>{const o=t[0]-e[0],a=t[1]-e[1],r=Math.hypot(o,a);if(!(r>n))return null;const l=o/r,c=a/r,h=t=>Math.abs((t[0]-e[0])*c-(t[1]-e[1])*l);if(h(i)>n||h(s)>n)return null;const d=mm(i,e,l,c),p=mm(s,e,l,c),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>n?{lo:u,hi:_}:null},fm=(e,t,i)=>Math.min(e.hi,t.hi)-Math.max(e.lo,t.lo)>i,vm=e=>{const t=new Set(["id","a","b","cm"]);return Object.keys(e||{}).every(e=>t.has(e))},ym=(e,t,i,s)=>{const n=e.b[0]-e.a[0],o=e.b[1]-e.a[1],a=Math.hypot(n,o);if(!(a>s))return!0;const r=n/a,l=o/a,c=wl(e.cm,i.cellCm,i.gridPitch)/2;return t.some(t=>{const n=t?.center;if(!pm(n))return!0;const o=mm(n,e.a,r,l),h=Math.abs((n[0]-e.a[0])*l-(n[1]-e.a[1])*r),d=wl(Number(t.cm),i.cellCm,i.gridPitch)*("square"===t.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||o>=-d-s&&o<=a+d+s&&h<=c+d+s})},bm=(e,t,i)=>{if(e.host){const s=Tp(e,t,i.coordScale,i.cellCm,i.gridPitch).resolved;return s?{center:s.center,angle:s.angle,length:s.length}:null}const s=Number(e.x)*i.coordScale,n=Number(e.y)*i.coordScale,o=Number(e.angle),a=Number(e.length)*i.coordScale;return[s,n,o,a].every(Number.isFinite)&&a>0?{center:[s,n],angle:o,length:a}:null},wm=(e,t,i)=>{const s=e.angle*Math.PI/180,n=Math.cos(s),o=Math.sin(s),a=t.angle*Math.PI/180,r=Math.cos(a),l=Math.sin(a);if(Math.abs(n*l-o*r)>1e-6)return!1;const c=t.center[0]-e.center[0],h=t.center[1]-e.center[1];if(Math.abs(c*o-h*n)>i)return!1;const d=c*n+h*o;return Math.max(-e.length/2,d-t.length/2)({...e,a:[e.a[0]*t,e.a[1]*t],b:[e.b[0]*t,e.b[1]*t]}),xm=(e,t,i,s)=>{const n=`~r-${(e=>{let t=2166136261;for(let i=0;i>>0;return t.toString(36)})(`${e}|${t[0].toFixed(9)},${t[1].toFixed(9)}|${i[0].toFixed(9)},${i[1].toFixed(9)}`)}`,o=`${e.slice(0,Math.max(1,64-n.length))}${n}`;let a=o;for(let e=2;s.has(a);e++){const t=`-${e}`;a=`${o.slice(0,64-t.length)}${t}`}return s.add(a),a},$m=(e,t)=>{const i=[];for(const s of e){const e=i[i.length-1];!(e&&Math.abs(e.hi-s.lo)<=t&&e.safe===s.safe)||s.safe&&e.signature!==s.signature?i.push({lo:s.lo,hi:s.hi,a:s.a,b:s.b,safe:s.safe,signature:s.signature,roomIds:s.roomIds,finalCm:s.finalCm}):(e.hi=s.hi,e.b=s.b)}return i};function Sm(e,t,i,s,n){const o=i||[],a=Array.isArray(e?.partitions)?e.partitions:[],r=Array.isArray(e?.openings)?e.openings:[],l=Array.isArray(e?.room_drafts)?e.room_drafts:[],c={walls:o,partitions:a,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!a.length&&!l.length)return c;const h=Math.max(2e-4*n.gridPitch,1e-9);let d=o,p=a,u=r;const _=gc(t.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),m=((e,t,i,s)=>{const n=new Map(e.map(e=>[e.id,e])),o=new Set;for(const e of t){if(!n.get(e.id)||!Array.isArray(e.points)||e.points.length<2||e.segments.length!==e.points.length-1)continue;let t=!0;for(let n=0;n+10)){t=!1;break}const l=Math.hypot(a[0]-o[0],a[1]-o[1]);if(!(l>s)){t=!1;break}const c=i.flatMap(e=>{const t=e.cm>0?e.cm:ul;if(e.open||"outer"!==e.kind&&"shared"!==e.kind||t+se.lo-t.lo||e.hi-t.hi);let h=0;for(const e of c){if(e.lo>h+s)break;if(h=Math.max(h,e.hi),h>=l-s)break}h!m.has(e.id));let f=0,v=0;const y=new Map(t.partitions.map(e=>[e.id,e]));for(const e of[...a].sort((e,t)=>e.id.localeCompare(t.id))){if(!p.some(t=>t.id===e.id))continue;if(!vm(e))continue;const i=y.get(e.id);if(!(i&&pm(i.a)&&pm(i.b)&&Number.isFinite(i.cm)&&i.cm>0))continue;const[o,a]=um(i.a,i.b)<=0?[[i.a[0],i.a[1]],[i.b[0],i.b[1]]]:[[i.b[0],i.b[1]],[i.a[0],i.a[1]]],r=a[0]-o[0],l=a[1]-o[1],c=Math.hypot(r,l);if(!(c>h))continue;const _=r/c,g=l/c,b=gc(t.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),w=b.flatMap(e=>{if(e.open||"outer"!==e.kind&&"shared"!==e.kind)return[];const t=gm(o,a,e.a,e.b,h);return t?[{interval:e,range:t}]:[]});if(!w.length)continue;const k=u.filter(e=>"partition"===e.host?.kind&&e.host.id===i.id).map(e=>({opening:e,resolved:Tp(e,t.partitions,n.coordScale,n.cellCm,n.gridPitch).resolved}));if(k.some(e=>!e.resolved))continue;const x=[0,c];for(const{range:e}of w)x.push(e.lo,e.hi);const $=x.slice(),S=new Map;for(const{opening:e,resolved:t}of k){const i=mm(t.center,o,_,g),s={lo:i-t.length/2,hi:i+t.length/2};S.set(e.id,s),$.push(Math.max(0,s.lo),Math.min(c,s.hi))}const M=[...new Set($.filter(e=>Number.isFinite(e)&&e>=-h&&e<=c+h).map(e=>Math.max(0,Math.min(c,e)).toFixed(9)))].map(Number).sort((e,t)=>e-t),C=p.map(e=>km(e,n.coordScale)).filter(e=>e.id!==i.id),D=t.room_drafts.filter(e=>!m.has(e.id)),P=[];for(let e=0;e+1h))continue;const r=_m(o,_,g,s),l=_m(o,_,g,a),c=w.filter(({range:e})=>e.lo<=s+h&&e.hi>=a-h).map(({interval:e})=>e),d=new Map;for(const e of c)d.set(e.roomId,e);const p=[...d.values()],u=new Set(p.map(e=>e.kind)),m=new Set(p.map(e=>e.cm>0?e.cm:ul)),f=p[0]?.kind,v=[...d.keys()].sort(),y=1===u.size&&1===m.size&&("outer"===f&&1===v.length||"shared"===f&&2===v.length),b={id:i.id,a:r,b:l,cm:i.cm},k=C.some(e=>!!gm(r,l,e.a,e.b,h)),x=D.some(e=>e.points.some((t,i)=>i+10?p[0].cm:ul:0,T=y?Math.max(S,i.cm):i.cm;P.push({lo:s,hi:a,a:r,b:l,safe:$,signature:$?`${f}|${v.join(",")}|${T}`:"",roomIds:v,roomCm:S,finalCm:T})}if(!P.some(e=>e.safe))continue;const T=new Map;for(const{opening:e,resolved:t}of k){const s=S.get(e.id),o=x.some(e=>e>s.lo+h&&efm(e,s,h)),r=new Set(a.filter(e=>e.safe).map(e=>e.signature));if(!o&&a.length>0&&a.every(e=>e.safe)&&1===r.size&&Ep(t,b,h)){T.set(e.id,{resolved:t,signature:[...r][0]});continue}const l=Dp(i,n.cellCm,n.gridPitch),c={lo:s.lo-l,hi:s.hi+l};for(const e of P)fm(e,c,h)&&(e.safe=!1,e.signature="")}let I=!0;for(;I;){I=!1;for(const[e,t]of[...T.entries()]){const s=S.get(e),o=P.filter(e=>fm(e,s,h));if(o.length>0&&o.every(e=>e.safe)&&o.every(e=>e.signature===t.signature))continue;T.delete(e);const a=Dp(i,n.cellCm,n.gridPitch),r={lo:s.lo-a,hi:s.hi+a};for(const e of P)fm(e,r,h)&&(e.safe=!1,e.signature="");I=!0}}const R=$m(P,h),A=R.filter(e=>e.safe),z=R.filter(e=>!e.safe);if(!A.length)continue;if(p.length-1+z.length>2e3)continue;let F=d;for(const e of A){const[t,s]=um(i.a,i.b)<=0?[e.a,e.b]:[e.b,e.a];F=Ll(F,t,s,e.finalCm,n.pitch,n.coordScale)}if(F=vc(t.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),F.length>500)continue;const E=new Set(p.map(e=>e.id));E.delete(i.id);const N=z.map((e,t)=>{const s=[e.a[0]/n.coordScale,e.a[1]/n.coordScale],o=[e.b[0]/n.coordScale,e.b[1]/n.coordScale],a=0===t?i.id:xm(i.id,s,o,E);return E.add(a),{id:a,a:s,b:o,cm:i.cm}}),O=p.findIndex(e=>e.id===i.id),H=p.slice();H.splice(O,1,...N);const L=H.map(e=>km(e,n.coordScale)),B=new Map;let q=!0;for(const{opening:e,resolved:t}of k){const i=T.get(e.id);if(i){const t=Np(e,i.resolved,n.coordScale),{host:s,...o}=t;B.set(e.id,o);continue}const s=S.get(e.id),o=N[z.findIndex(e=>e.lo<=s.lo+h&&e.hi>=s.hi-h)],a=o&&L.find(e=>e.id===o.id);if(!o||!a){q=!1;break}const r=a.b[0]-a.a[0],l=a.b[1]-a.a[1],c=Math.hypot(r,l),d=mm(t.center,a.a,r/c,l/c),p={...Np(e,t,n.coordScale),host:{kind:"partition",id:o.id,t:d/c}};if(!Ip(p,L,n.coordScale,n.cellCm,n.gridPitch).resolved){q=!1;break}B.set(e.id,p)}if(!q)continue;const W=u.map(e=>B.get(e.id)||e),j=[...T.keys()].map(e=>B.get(e)),U=j.map(e=>bm(e,L,n));if(U.some(e=>!e))continue;let G=!1;for(let e=0;ee.id));for(const e of W){if(V.has(e.id))continue;const t=bm(e,L,n);t&&U.some(e=>wm(e,t,h))&&(G=!0)}if(G)continue;const K=Bc(t.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale);[...T.entries()].every(([e,t])=>{const i=B.get(e),s=jc(K,{x:i.x*n.coordScale,y:i.y*n.coordScale,angle:i.angle,length:i.length*n.coordScale},!0),o=[s.negative,s.positive].filter(e=>!!e?.full),a=t.signature.split("|")[1].split(","),r=new Set(o.map(e=>e.roomId));return o.length===a.length&&r.size===a.length&&a.every(e=>r.has(e))})&&(d=F,p=H,u=W,f+=A.length,v+=T.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:v,removedDrafts:m.size}}const Mm=e=>JSON.parse(JSON.stringify(e)),Cm=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Dm=(e,t)=>t.length>0&&t.length<=35&&("space"===e?/^[a-z0-9_-]+$/.test(t):/^[A-Za-z0-9_-]+$/.test(t));function Pm(e,t){let i=String(t??"");const s=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`^${s}_(.+)_([0-9a-f]{8})$`);let o=0;for(;o<16;o++){const e=n.exec(i);if(!e)return{root:i,layers:o,bounded:!1};i=e[1]}return{root:i,layers:o,bounded:n.test(i)}}const Tm=(e,t,i)=>{const s=e.get(t)||[];s.push(i),e.set(t,s)};const Im=1e-9;class Rm extends Error{constructor(e,t=""){super(t?`${e}: ${t}`:e),this.reason=e,this.name="WallSegmentModelError"}}const Am=e=>JSON.parse(JSON.stringify(e)),zm=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])),Fm=e=>[Number(e[0]),Number(e[1])],Em=e=>`${Number(e[0]).toFixed(12)},${Number(e[1]).toFixed(12)}`,Nm=(e,t)=>{const i=Em(e),s=Em(t);return iEm(e)<=Em(t)?[Fm(e),Fm(t)]:[Fm(t),Fm(e)],Hm=(e,t)=>Math.hypot(t[0]-e[0],t[1]-e[1]),Lm=e=>{const t=[[Number(e.points[0][0]),Number(e.points[0][1])]],i=[];for(let s=1;s{const i=new Set,s=e=>{const t=Number(e);if(!Number.isFinite(t))return;const s=t/Fh;Math.abs(s-Math.round(s))>=Jr&&i.add(t.toFixed(12))};for(const t of Array.isArray(e?.rooms)?e.rooms:[])for(const e of hi(t)||[])s(e[0]),s(e[1]);for(const t of[e?.wall_segments,e?.walls])for(const e of Array.isArray(t)?t:[])s(e?.a?.[0]),s(e?.a?.[1]),s(e?.b?.[0]),s(e?.b?.[1]);for(const e of t)s(e?.[0]),s(e?.[1]);return i.size},qm=(e,t,i)=>{const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;return o>1e-18?((e[0]-t[0])*s+(e[1]-t[1])*n)/o:0},Wm=(e,t,i)=>{const s=Math.max(0,Math.min(1,qm(e,t,i)));return Math.hypot(e[0]-(t[0]+(i[0]-t[0])*s),e[1]-(t[1]+(i[1]-t[1])*s))},jm=(e,t,i,s,n=Im)=>{const o=t[0]-e[0],a=t[1]-e[1],r=Math.hypot(o,a);if(r<=n)return 0;const l=t=>Math.abs((t[0]-e[0])*a-(t[1]-e[1])*o)/r;if(l(i)>n||l(s)>n)return 0;const c=qm(i,e,t),h=qm(s,e,t);return Math.max(0,Math.min(1,Math.max(c,h))-Math.max(0,Math.min(c,h)))*r},Um=e=>{const t=(new TextEncoder).encode(e),i=8*t.length,s=64*Math.ceil((t.length+9)/64),n=new Uint8Array(s);n.set(t),n[t.length]=128;const o=new DataView(n.buffer);o.setUint32(s-8,Math.floor(i/4294967296),!1),o.setUint32(s-4,i>>>0,!1);const a=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),c=(e,t)=>e>>>t|e<<32-t;for(let e=0;e>>3,n=c(i,17)^c(i,19)^i>>>10;l[e]=l[e-16]+s+l[e-7]+n>>>0}let[t,i,s,n,h,d,p,u]=r;for(let e=0;e<64;e++){const o=u+(c(h,6)^c(h,11)^c(h,25))+(h&d^~h&p)+a[e]+l[e]>>>0,r=t&i^t&s^i&s;u=p,p=d,d=h,h=n+o>>>0,n=s,s=i,i=t,t=o+((c(t,2)^c(t,13)^c(t,22))+r>>>0)>>>0}r[0]=r[0]+t>>>0,r[1]=r[1]+i>>>0,r[2]=r[2]+s>>>0,r[3]=r[3]+n>>>0,r[4]=r[4]+h>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+u>>>0}const h=new Uint8Array(32),d=new DataView(h.buffer);for(let e=0;e{const t="abcdefghijklmnopqrstuvwxyz234567";let i=0,s=0,n="";for(const o of e)for(s=s<<8|o,i+=8;i>=5;)n+=t[s>>>i-5&31],i-=5;return i&&(n+=t[s<<5-i&31]),n},Vm=(e,t,i,s,n="")=>{const[o,a]=Om(t,i),r=`${e}|${Em(o)}|${Em(a)}|${[...s].sort().join(",")}${n}`;return`wall-${Gm(Um(r)).slice(0,20)}`};let Km=0;const Ym=e=>{for(let t=0;t<1e3;t++){const t=globalThis.crypto?.randomUUID?.(),i=`wall-${t||Gm(Um(`${Date.now()}|${Km++}|${Math.random()}`)).slice(0,26)}`;if(!e.has(i))return i}throw new Rm("duplicate-id","id factory exhausted")},Jm=e=>{const t=new Map;for(const i of Array.isArray(e?.wall_segments)?e.wall_segments:[])if(i&&"string"==typeof i.id&&zm(i.a)&&zm(i.b)){if(t.has(i.id))throw new Rm("duplicate-id",i.id);t.set(i.id,i)}return t},Xm=(e,t,i)=>{const s=(i,s)=>{const n=e[0]-i[0],o=e[1]-i[1];return Math.abs(t[0]-s[0]-n)<=Im&&Math.abs(t[1]-s[1]-o)<=Im?[n,o]:null};return s(i.a,i.b)||s(i.b,i.a)},Zm=(e,t)=>{const i=Array.isArray(e.rooms)?e.rooms:[],s=vd(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]),n=s.length?s:yd(i,null,1,.04*Fh,!0),o=[];for(const e of i){const i=hi(e),s=Array.isArray(e?.wall_ids)?e.wall_ids:[];if(i&&s.length===i.length)for(let e=0;e"partition"!==e.host?.kind&&[Number(e.x),Number(e.y)].every(Number.isFinite)&&Number.isFinite(Number(e.angle))&&Number(e.length)>0&&n.some(t=>Wm([Number(e.x),Number(e.y)],[t[0],t[1]],[t[2],t[3]])<=.04*Fh)):[],l=r.map(e=>{const t=Number(e.angle)*Math.PI/180,i=[Math.cos(t),Math.sin(t)],s=Number(e.length)/2,n=[Number(e.x),Number(e.y)];return{a:[n[0]-i[0]*s,n[1]-i[1]*s],b:[n[0]+i[0]*s,n[1]+i[1]*s]}}),c=(e,t)=>r.some(i=>{const s=[Number(i.x),Number(i.y)];if(!El(e,t,Number(i.angle)))return!1;if(Wm(s,e,t)>.02*Fh)return!1;const n=Hm(e,t),o=qm(s,e,t)*n,a=Number(i.length)/2;return o+a>=-Im&&o-a<=n+Im}),h=new Map,d=[];for(const s of i){const r=String(s?.id||""),p=hi(s);if(!r||!p||p.length<3)throw new Rm("invalid-room",r);const u=ac(i,r,a,Fh,1,[...e.walls||[],...l]);if(!u||u.poly.length<3)throw new Rm("invalid-room",r);const _=Array.isArray(s.wall_ids)?s.wall_ids:[],m=_.length===p.length;let g=null,f=m;if(f)for(let e=0;eIm||Math.abs(s[1]-g[1])>Im)){f=!1;break}g=s}const v=[];for(let e=0;e2)throw new Rm("third-owner",a);const d=m?_[u.parent[e]]:void 0;if("string"==typeof d&&d){const n=t.get(d);f||!n||jm(i,s,n.a,n.b)>Im?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:Fm(p[u.parent[e]]),b:Fm(p[(u.parent[e]+1)%p.length])})}const g=u.parent[e];l.parentKeys.add(Ml(p[g],p[(g+1)%p.length],Fh));const y=[(i[0]+s[0])/2,(i[1]+s[1])/2],b=e=>e.some(e=>Wm(y,[e[0],e[1]],[e[2],e[3]])<=.04*Fh);l.zeroWall||=b(o)||b(n)&&!c(i,s),v.push(a)}const y={...s};delete y.wall_ids,d.push({...y,poly:u.poly.map(e=>[e[0],e[1]]),wall_ids:v})}return{atoms:[...h.values()].sort((e,t)=>e.key.localeCompare(t.key)),rooms:d}},Qm=(e,t,i,s,n)=>{const o=new Map([...i.values()].map(e=>[Nm(e.a,e.b),e])),a=(e=>{const t=new Map;for(const i of e)"wall"===i.host?.kind&&t.set(i.host.id,(t.get(i.host.id)||0)+1);return t})(e.openings||[]),r=new Map;for(const e of t){const t=n?.get(e.key);if(t){const s=i.get(t);if(!s)throw new Rm("duplicate-id",t);r.set(e,s);continue}if(e.preferredIds.size>1)throw new Rm("duplicate-id",[...e.preferredIds].sort().join(","));const s=[...e.preferredIds][0],l=s?i.get(s):void 0;if(l){r.set(e,l);continue}const c=s?e.preferredCarriers.get(s):void 0;if(s&&c){r.set(e,{id:s,a:c.a,b:c.b,cm:0});continue}const h=o.get(e.key);if(h){r.set(e,h);continue}const d=[...i.values()].filter(t=>jm(e.a,e.b,t.a,t.b)>Im),p=[...new Map(d.map(e=>[e.id,e])).values()];if(p.sort((e,t)=>(a.get(t.id)||0)-(a.get(e.id)||0)||Hm(t.a,t.b)-Hm(e.a,e.b)||e.id.localeCompare(t.id)),p[0])r.set(e,p[0]);else if(1===e.positionalIds.size){const t=i.get([...e.positionalIds][0]);t&&r.set(e,t)}}const l=new Map;for(const[e,t]of r){const i=l.get(t.id)||[];i.push(e),l.set(t.id,i)}for(const[e,t]of l){const s=i.get(e)||r.get(t[0]);if(!s)throw new Rm("duplicate-id",e);const n=[(s.a[0]+s.b[0])/2,(s.a[1]+s.b[1])/2];t.sort((e,t)=>{const i=Wm(n,e.a,e.b)<=Im?0:1,o=Wm(n,t.a,t.b)<=Im?0:1;if(i!==o)return i-o;return(Wm(s.a,e.a,e.b)<=Im?0:1)-(Wm(s.a,t.a,t.b)<=Im?0:1)||e.key.localeCompare(t.key)}),t[0].id=e}const c=(e=>{const t=new Set;for(const i of[e.rooms,e.openings,e.decor,e.room_drafts,e.partitions,e.wall_columns])for(const e of Array.isArray(i)?i:[])"string"==typeof e?.id&&e.id&&t.add(e.id);for(const i of Array.isArray(e.room_drafts)?e.room_drafts:[])for(const e of Array.isArray(i?.segments)?i.segments:[])"string"==typeof e?.id&&e.id&&t.add(e.id);return t})(e);for(const e of t)if(e.id){if(c.has(e.id))throw new Rm("duplicate-id",e.id);c.add(e.id)}const h=t.filter(e=>!e.id);if(s){const t=h.map(t=>{const[i,s]=Om(t.a,t.b),n=`${String(e.id||"")}|${Em(i)}|${Em(s)}|${[...t.owners].sort().join(",")}`,o=Gm(Um(n));return{atom:t,seed:n,digest:o,base:`wall-${o.slice(0,20)}`}}).sort((e,t)=>e.digest.localeCompare(t.digest)||e.atom.key.localeCompare(t.atom.key)),i=new Map;for(const e of t){const t=i.get(e.digest);if(t&&t!==e.seed)throw new Rm("duplicate-id",e.digest);i.set(e.digest,e.seed);let s=1,n=e.base;for(;c.has(n);)n=`${e.base}-${++s}`;e.atom.id=n,c.add(n)}}else for(const e of h)e.id=Ym(c),c.add(e.id)},eg=(e,t)=>{if("partition"===e.host?.kind)return null;const i=[Number(e.x),Number(e.y)];if(!i.every(Number.isFinite))return null;const s=t=>{if(!(Number(t.cm)>0))return!1;const s=qm(i,t.a,t.b),n=Hm(t.a,t.b),o=Number(e.length)/2;return s>=-Im&&s<=1+Im&&Wm(i,t.a,t.b)<=.02*Fh&&El(t.a,t.b,Number(e.angle))&&Number.isFinite(o)&&o>=0&&s*n-o>=-Im&&s*n+o<=n+Im},n="wall"===e.host?.kind?t.find(t=>t.id===e.host.id):null,o=n&&s(n)?[n]:t.filter(s);if(1!==o.length)return null;const a=o[0];return{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,qm(i,a.a,a.b)))}},tg=(e,t)=>{const i=[Number(e.x),Number(e.y)];if(!i.every(Number.isFinite))return null;const s=Number(e.length)/2,n=t.filter(t=>{if(!(Number(t.cm)>0))return!1;const n=qm(i,t.a,t.b),o=Hm(t.a,t.b);return n>=-Im&&n<=1+Im&&Wm(i,t.a,t.b)<=.02*Fh&&El(t.a,t.b,Number(e.angle))&&Number.isFinite(s)&&s>=0&&n*o-s>=-Im&&n*o+s<=o+Im}),o=(t=>{if(!t.length)return null;const s="wall"===e.host?.kind?t.find(t=>t.id===e.host.id):null;return s||[...t].sort((e,t)=>Wm(i,e.a,e.b)-Wm(i,t.a,t.b)||Number(t.cm)-Number(e.cm)||(e.idt.id?1:0))[0]})(n);return o?{kind:"wall",id:o.id,t:Math.max(0,Math.min(1,qm(i,o.a,o.b)))}:null},ig=(e,t,i)=>{const s=Jm(e),{atoms:n,rooms:o}=Zm(e,s);Qm(e,n,s,t,i);const a=n.map(t=>{const i=t.id?s.get(t.id):void 0,n=((e,t,i)=>{if(t.zeroWall)return 0;const s=Array.isArray(e.walls)?e.walls:[],n=Ml(t.a,t.b,Fh),o=s.filter(e=>Number(e.cm)>0&&(!(e.key!==n&&!t.parentKeys.has(e.key))||!(!zm(e.a)||!zm(e.b))&&jm(t.a,t.b,e.a,e.b)>=Hm(t.a,t.b)-Im)).map(e=>Math.max(1,Math.min(100,Number(e.cm))));if(new Set(o.map(e=>e.toFixed(9))).size>1)throw new Rm("thickness-conflict",t.key);if(o.length)return o[0];const a=zl(s,t.a,t.b,Fh,1);return a>0?a:Number(i?.cm)>0?Number(i.cm):0})(e,t,i);return{...i||{},id:t.id,a:[...t.a],b:[...t.b],cm:n}}),r=new Map(n.map(e=>[e.key,e.id]));for(const e of o)e.wall_ids=e.wall_ids.map(e=>r.get(e));e.rooms=o,e.wall_segments=a,e.walls=a.filter(e=>e.cm>0).map(e=>({key:Ml(e.a,e.b,Fh),cm:e.cm,a:[...e.a],b:[...e.b]})),e.walls.length||delete e.walls,delete e.open_spans;for(const t of e.rooms)delete t.open_to;return((e,t)=>{const i=new Set;for(const t of[e.rooms,e.openings,e.decor,e.room_drafts,e.partitions,e.wall_columns,e.wall_segments])for(const e of Array.isArray(t)?t:[])"string"==typeof e?.id&&e.id&&i.add(e.id);for(const s of Array.isArray(e.room_drafts)?e.room_drafts:[]){const n=Array.isArray(s.segments)?s.segments:[];for(let o=0;o{const s=Array.isArray(e.openings)?e.openings:[];for(const e of s){if("partition"===e.host?.kind)continue;if(!e.host&&!i){const i=eg(e,t);i&&(e.host=i);continue}const s=eg(e,t)??(i?tg(e,t):null);if(s)e.host=s;else{if(!i)throw new Rm("opening-host",e.id);delete e.host}}})(e,a,t),a.reduce((e,t)=>e+(s.has(t.id)?0:1),0)};function sg(e,t={}){const i=JSON.stringify(e),s=Am(e);pl(s);let n=0;const o=Number(s?.model_version||0)<9;for(const e of Array.isArray(s?.spaces)?s.spaces:[]){const i=t.lineageSpaceId===String(e?.id||"")?t.lineageHints:void 0;n+=ig(e,o,i)}return s.model_version=9,pl(s),{config:s,changed:i!==JSON.stringify(s),migratedSegments:n}}const ng=e=>!!e&&"object"==typeof e&&!Array.isArray(e);function og(e,t){const i=(e,t)=>{if(Array.isArray(e)&&Array.isArray(t)){const s=new Map;for(const t of e)ng(t)&&"string"==typeof t.id&&t.id&&s.set(t.id,t);const n=t.map((t,n)=>{if(ng(t)&&"string"==typeof t.id&&t.id){const e=s.get(t.id);return e?i(e,t):Am(t)}return n[t,t in e?i(e[t],s):Am(s)]);for(const t of Object.keys(e))delete e[t];for(const[t,i]of s)e[t]=i;return e}return Am(t)};return i(e,t)}const ag=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),rg=(e,t,i)=>Math.min(i,Math.max(t,e)),lg=e=>Th({spaces:[e]})[0],cg=(e,t,i)=>{const s=i>0?i:1,n=e=>`${(e[0]/s).toFixed(12)},${(e[1]/s).toFixed(12)}`,o=n(e),a=n(t);return o0?a:1,l=Math.max(s*r*.02,1e-9),c=[];for(const t of e||[])for(const e of hi(t)||[])c.push([e[0],e[1]]);const h=[];for(const e of i||[])Array.isArray(e)&&e.length>=4&&e.slice(0,4).every(Number.isFinite)&&h.push([e[0],e[1]],[e[2],e[3]]);const d=(e,t)=>t.some(t=>Math.hypot(e[0]-t[0],e[1]-t[1])<=2*l),p=new Map;for(const a of e||[]){if(!a?.id)continue;const u=mc(e,a.id,t,i,s,n,o,r);if(u)for(let e=0;e0&&_>0&&a===m&&_!==a))continue;const g=u.poly[s],f=u.poly[(s+1)%u.poly.length],v=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(v>l&&v<.5*o-1e-9*o))continue;if(d(g,h)||d(f,h))continue;if(d(g,c)&&d(f,c))continue;const y=cg(g,f,r),b=p.get(y);b?b.targets.add(a):p.set(y,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([a])})}}}let u=t.slice();const _=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1])<=2*l,m=(e,t)=>{if(!Array.isArray(e.a)||!Array.isArray(e.b)||e.a.length<2||e.b.length<2)return!1;const i=[Number(e.a[0])*r,Number(e.a[1])*r],s=[Number(e.b[0])*r,Number(e.b[1])*r];return!![...i,...s].every(Number.isFinite)&&(_(i,t.a)&&_(s,t.b)||_(i,t.b)&&_(s,t.a))};for(const e of[...p.values()].filter(e=>1===e.targets.size).sort((e,t)=>cg(e.a,e.b,r).localeCompare(cg(t.a,t.b,r)))){const t=[...e.targets][0],i=u.filter(t=>m(t,e));if(new Set(i.map(e=>e.cm)).size>1)continue;let n=!1;u=u.map(i=>m(i,e)?(n=!0,i.cm===t?i:{...i,cm:t}):i),n||(u=Ll(u,e.a,e.b,t,s,r))}return u}const dg=e=>{const t=[];for(const i of e||[]){const e=hi(i);if(e?.length)for(let i=0;i"string"==typeof e?.id?e.id:"").filter(Boolean),h=new Set(c),d=new Map;for(const e of c){const t=Pm("space",e);t.layers>0&&Dm("space",t.root)&&Tm(d,t.root,e)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const e of r){const t="string"==typeof e?.id?e.id:"";if(!t)continue;const i=new Map;for(const s of Array.isArray(e.rooms)?e.rooms:[]){const e="string"==typeof s?.id?s.id:"";if(!e)continue;const n=Pm("room",e);n.layers>0&&Dm("room",n.root)&&Tm(i,n.root,e),u.has(e)||u.set(e,t),_.has(e)||_.set(e,String(s.name||""));const o="string"==typeof s.area?s.area:"";if(o){const i=m.get(o)||[];i.push({spaceId:t,roomId:e}),m.set(o,i)}}p.set(t,i)}const g=new Set(u.keys()),f=new Map,v=new Map;for(const e of l)"string"==typeof e?.id&&e.id&&(!0===e.removed?v.set(e.id,e):f.set(e.id,e));const y={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},b=new Set,w=e=>h.has(e)?null:(()=>{const t=Pm("space",e).root;if(!Dm("space",t))return null;const i=d.get(t)||[];return 1===i.length?i[0]:null})(),k=(e,t)=>{const i=Pm("room",e).root;if(!Dm("room",i))return null;const s=p.get(t)?.get(i)||[];return 1===s.length?s[0]:null},x=e=>{const t=i.effectiveAreaByMarker?.[e]||"",s=t&&m.get(t)||[];return 1===s.length?s[0]:null},$=(e,t,i)=>{if("string"==typeof e.room_id&&e.room_id&&!g.has(e.room_id)){const s=t?k(e.room_id,t):null;s?(e.room_id=s,y.roomRefsRemapped++):i?(e.room_id=i.roomId,y.roomRefsRemapped++):!0!==e.removed&&delete e.room_id}const s=e.vacuum?.segment_map;if(s&&"object"==typeof s&&!Array.isArray(s))for(const e of Object.keys(s)){const i=s[e];if("string"!=typeof i||!i||g.has(i))continue;const n=t?k(i,t):null;n?(s[e]=n,y.roomRefsRemapped++):y.nestedRefsUnresolved++}};for(const e of l){const t="string"==typeof e?.id?e.id:"";if(!t)continue;const i="string"==typeof e.space?e.space:"",s=i&&!h.has(i),o=n[t],a="string"==typeof o?.s?o.s:"",r=a&&!h.has(a),l=!0===e.removed;let c=i&&h.has(i)?i:null,d=null,p=!1;if(s){const s=w(i);s?(e.space=s,c=s,p=!0,y.spaceRefsRemapped++):l||"virtual"===e.binding?l||(delete e.space,c=null,y.markersDetached++):(d=x(t),d?(e.space=d.spaceId,c=d.spaceId,y.spaceRefsRemapped++):(delete e.space,c=null,y.markersDetached++))}l&&!p||$(e,c,d),r&&p&&a===i&&c&&(n[t]={...o,s:c},y.positionsRemapped++,b.add(t))}for(const e of Object.keys(n)){if(b.has(e))continue;const t=n[e],i="string"==typeof t?.s?t.s:"";if(!i||h.has(i))continue;const s=w(i);if(s){if(e.startsWith("rl_")){const i=e.slice(3),o=k(i,s);if(o){const i=`rl_${o}`;Cm(n,i)||(n[i]={...t,s:s}),delete n[e],y.roomRefsRemapped++,y.positionsRemapped++;continue}}n[e]={...t,s:s},y.positionsRemapped++}}const S=i.ownerRoster,M=!0===S?.authoritative,C=new Set(S?.deviceIds||[]),D=new Set(S?.entityIds||[]),P=new Set(Array.isArray(s.settings?.known_devices)?s.settings.known_devices.filter(e=>"string"==typeof e):[]),T=(e,t="")=>{const i=String(S?.names?.[e]||t||"").trim();return i&&i!==e?i:""},I=(e,t,i,s="",n)=>({id:e,spaceId:t,kind:i,name:T(e,s),...n?{reason:n}:{}}),R=e=>{"room_label"===e?y.orphanRoomLabelsRemoved++:"group"===e?y.orphanGroupPositionsRemoved++:y.orphanDevicePositionsRemoved++},A=new Set;for(const[e,t]of Object.entries(n)){const s="string"==typeof t?.s?t.s:"";if(!s||h.has(s))continue;let o,a;const r=f.get(e),l=v.get(e);if(r)o=I(e,s,"device",String(r.name||"")),a="live";else if(l)o=I(e,s,"device",String(l.name||"")),a="absent";else if(e.startsWith("rl_")){const t=e.slice(3);o=I(e,s,"room_label",_.get(t)||""),a=g.has(t)?"live":"absent"}else if(e.startsWith("lg_")){const t=e.slice(3);o=I(e,s,"group"),a=D.has(t)?"live":M?"absent":"unverified","unverified"===a&&(o.reason="registry_unavailable")}else C.has(e)?(o=I(e,s,"device"),a="live"):P.has(e)?(o=I(e,s,"device"),a=M?"absent":"unverified","unverified"===a&&(o.reason="registry_unavailable")):(o=I(e,s,"unknown","","unknown_owner"),a="unverified");if("absent"!==a){if("live"===a){if(y.liveMissingPositions.push(o),!0===i.removeLiveMissingPositions){delete n[e],y.liveMissingPositionsRemoved++,R(o.kind),y.removedPositions.push(o);continue}}else y.unverifiedPositions.push(o);y.positionsUnresolved++,A.add(s)}else delete n[e],R(o.kind),y.removedPositions.push(o)}return y.deadSpaceIds=[...A].sort((e,t)=>e.localeCompare(t)),{config:s,layout:n,report:y,changed:JSON.stringify(s)!==o||JSON.stringify(n)!==a}}(e,t,i),a=o.config,r=cl(a,o.layout);pl(a),dl(o.layout);const l=JSON.stringify(e||{}),c=JSON.stringify(t||{}),h=Number.isInteger(Number(a.model_version))?Number(a.model_version):0,d=(e=>{let t=0,i=0,s=0;for(const i of e.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",t++),Array.isArray(i.controls)){const e="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=e?i.controls.filter(t=>t!==e):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,t++)}const e=i.vacuum;e&&ag(e,"trail")&&(["never","cleaning","always"].includes(e.trail_mode)||(e.trail_mode=!1===e.trail?"never":"cleaning"),delete e.trail,t++)}for(const n of e.spaces||[]){const e=n.settings;"glow"===e?.fill_mode&&("boolean"!=typeof e.glow_enabled&&(e.glow_enabled=!0),e.fill_mode="none",t++,i++);for(const e of n.rooms||[]){const i=e.settings;"glow"===i?.fill_mode&&("boolean"!=typeof i.glow&&(i.glow=!0),delete i.fill_mode,t++,s++)}if(ag(n,"segments")&&(delete n.segments,t++),ag(n,"plan_scale")){const e=Number(n.plan_scale);Number.isFinite(e)&&e>=Mh&&e<=Ch&&(ag(n,"plan_scale_x")||(n.plan_scale_x=e),ag(n,"plan_scale_y")||(n.plan_scale_y=e),delete n.plan_scale,t++)}const o=Number(n.cell_cm);ag(n,"cell_cm")&&(!Number.isFinite(o)||o<.1||o>1e3)&&(n.cell_cm=Number.isFinite(o)&&o>0?rg(o,.1,1e3):5,t++);const a=Number(n.cell_cm),r=Number.isFinite(a)&&a>0?a:5;for(const e of n.wall_columns||[])if("circle"===e.shape)ag(e,"angle")&&(delete e.angle,t++);else if("square"===e.shape&&ag(e,"angle")){const i=((Number(e.angle)||0)%90+90)%90;e.angle!==i&&(e.angle=i,t++)}for(const e of n.decor||[]){if(ag(e,"width")){const i=Number(e.width);(ag(e,"width_cm")||Number.isFinite(i))&&(ag(e,"width_cm")||(e.width_cm=Number(rg(i/zh*r,.1,100).toFixed(6))),delete e.width,t++)}if("rect"!==e?.kind&&"ellipse"!==e?.kind||!0!==e.fill||(ag(e,"fill_color")||(e.fill_color=e.color||"#607d8b",t++),ag(e,"fill_opacity")||(e.fill_opacity=.25,t++)),"text"!==e?.kind)continue;void 0===e.size_cm?(e.size_cm=Number(rg(ts*is(e)/zh*r,.1,2e3).toFixed(6)),delete e.scale,delete e.size,t++):(ag(e,"scale")||ag(e,"size"))&&(delete e.scale,delete e.size,t++);let i=String(e.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(e=>!!Yi(e[1]));if(!(ag(e,"entity")||ag(e,"attr")||ag(e,"unit")))continue;const n=String(e.unit??"").trim(),o="state"===String(e.attr??"").trim().toLowerCase()?null:e.attr,a=s||n?"":Ji(e.entity,o);if(s||a){if(a){const t=i.indexOf("{}");i=t>=0?i.slice(0,t)+a+i.slice(t+2):`${i}${i?" ":""}${a}`,e.text=i}delete e.entity,delete e.attr,delete e.unit,t++}}}return{total:t,glowSpaces:i,glowRooms:s}})(a);let p=d.total;const u=(_=a.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=im(a.spaces||[],o.layout);let g=0,f=0,v=0,y="";const b=m.spaces.map(e=>{const t=function(e){const t=JSON.parse(JSON.stringify(e||{}));let i=Array.isArray(t.rooms)?t.rooms:[];const s=new Map;for(const e of i){const t=hi(e);if(t)for(let e=0;ee.key.localeCompare(t.key))){let t=null;for(const s of $a(e,i)){const e=r.get(_a(s.from));if(e&&e!==_a(s.to))continue;const n=wa(i,s);if(ka(i,n,s)){t=s,i=n;break}}t?(r.set(_a(t.from),_a(t.to)),n++,a=Math.max(a,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}t.rooms=i;for(const e of t.room_drafts||[]){if(!Array.isArray(e?.points)||e.points.length<2)continue;const t=e.points.map(e=>[...e]),i=t.slice(0,-1).flatMap((i,s)=>{const n=t[s+1],o=pa(i,n);return o?[{key:`${String(e.id||"")}:${s}`,a:[...i],b:[...n],axis:o.axis}]:[]});let s=t;for(const e of i){let t=null;for(const i of $a(e,[{poly:s}])){const e=s.map(e=>ba(e,i)),n=e.length>=4&&ma(e[0],e[e.length-1]),o=n?e.slice(0,-1):e;if(n?va(o):ya(o)){t=i,s=e;break}}t?(n++,a=Math.max(a,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}e.points=s}for(const e of t.partitions||[]){if(!Array.isArray(e?.a)||!Array.isArray(e?.b))continue;const i=pa(e.a,e.b);if(!i)continue;const s={key:String(e.id||ga(e.a,e.b)),a:[...e.a],b:[...e.b],axis:i.axis};let r=null;for(const i of $a(s,[{poly:[e.a,e.b]}])){const s=ba(e.a,i),n=ba(e.b,i),o=Math.hypot(n[0]-s[0],n[1]-s[1]);if(!(o>0))continue;const a=(t.openings||[]).filter(t=>"partition"===t?.host?.kind&&t.host.id===e.id).every(e=>{const t=Number(e.host.t),i=Number(e.length);return Number.isFinite(t)&&t>=0&&t<=1&&Number.isFinite(i)&&i>0&&t*o-i/2>=-1e-12&&t*o+i/2<=o+1e-12});if(a){r=i,e.a=s,e.b=n;break}}r?(n++,a=Math.max(a,Math.hypot(r.to[0]-r.from[0],r.to[1]-r.from[1]))):o++}return{space:t,report:{wallsStraightened:n,wallsStraightenSkipped:o,maxStraightenShift:a},changed:n>0}}(e);g+=t.report.wallsStraightened,f+=t.report.wallsStraightenSkipped;const i=Number(e?.cell_cm)>0?Number(e.cell_cm):5,s=t.report.maxStraightenShift/Fh*i;return s>v&&(v=s,y=String(e?.id||"")),t.space}),w=g?im(b,m.layout):{...m,spaces:b};a.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,$=0,S=0,M=0,C=0,D=0,P=0,T=0;for(let e=0;e0===Number(e.cm)),walls:t.walls||[]}),o=lg(t),r=lg(i);if(!o||!r)continue;const l=dg(o.rooms),c=dg(r.rooms),h=.02*zh,d=(t.wall_segments||[]).filter(e=>0===Number(e.cm)).map(e=>({...e,key:Ml(e.a,e.b,Fh),cm:0})),p=new Set(d.map(e=>e.key)),_=b_(t,o.rooms,$h,h);$+=_.length;for(const e of _){const t=[e[0]/$h,e[1]/$h],i=[e[2]/$h,e[3]/$h],s=Ml(t,i,Fh);p.has(s)||(p.add(s),d.push({key:s,a:t,b:i,cm:0}))}const m=d.length,g=Ol(d,l,c,Fh,$h),f=(e,t,i)=>{const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;if(!(o>0))return Math.hypot(e[0]-t[0],e[1]-t[1]);const a=Math.max(0,Math.min(1,((e[0]-t[0])*s+(e[1]-t[1])*n)/o));return Math.hypot(e[0]-(t[0]+s*a),e[1]-(t[1]+n*a))},v=g.filter(e=>{if(!Array.isArray(e.a)||!Array.isArray(e.b))return!1;const t=e.a.map(e=>e*$h),i=e.b.map(e=>e*$h);return c.some(([e,s])=>f(t,e,s)<=4*h&&f(i,e,s)<=4*h)});S+=Math.max(0,m-v.length);const y=(i.wall_segments||[]).filter(e=>Number(e.cm)>0);i.wall_segments=[...y,...v.map(({key:e,...t})=>t)];const b=v.map(e=>[e.a[0]*$h,e.a[1]*$h,e.b[0]*$h,e.b[1]*$h]),w=Array.isArray(t.walls)?t.walls.length:0;let k=Ol(t.walls,l,c,Fh,$h);k=hg(r.rooms,k,b,Fh,Number(i.cell_cm)>0?Number(i.cell_cm):5,zh,$h),k=vc(r.rooms,k,b,Fh,Number(i.cell_cm)>0?Number(i.cell_cm):5,zh,$h),k=Fl(k,i.rooms||[],Fh,1,b.map(e=>[e[0]/$h,e[1]/$h,e[2]/$h,e[3]/$h])),x+=Math.max(0,w-k.length),k.length?i.walls=k:delete i.walls;const I=cm(i.partitions||[],{pitch:Fh,geometry:dm(i)});I.merged&&(M+=I.merged,i.partitions=I.partitions,hm(i.openings,i.partitions,I.openingMoves,{coordScale:$h,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:zh}));const R=lg(i);if(R){const e=n(i,R,i.walls||[],b,{pitch:Fh,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:zh,coordScale:$h});e.partitionsReconciled&&(C+=e.partitionsReconciled,D+=e.openingsRehosted,e.partitions.length?i.partitions=e.partitions:delete i.partitions,e.openings.length?i.openings=e.openings:delete i.openings,e.walls.length?i.walls=e.walls:delete i.walls),e.removedDrafts&&(P+=e.removedDrafts,e.roomDrafts.length?i.room_drafts=e.roomDrafts:delete i.room_drafts)}JSON.stringify({zero:(i.wall_segments||[]).filter(e=>0===Number(e.cm)),walls:i.walls||[]})!==s&&T++}let I=0;h<=9&&(I=function(e){const t=sg(e);return og(e,t.config),{...t,config:e}}(a).migratedSegments);const R=pl(a),A=dl(w.layout),z=JSON.stringify(R)!==l||JSON.stringify(A)!==c;h<9&&z&&(R.model_version=9);const F=JSON.stringify(R)!==l||JSON.stringify(A)!==c,E=Number.isInteger(Number(R.model_version))?Number(R.model_version):h,N=F?k:{...k,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},O=F?o.report:{...o.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:R,layout:A,report:{...N,modelFrom:h,modelTo:E,migrated:F?p:0,glowSpacesMigrated:F?d.glowSpaces:0,glowRoomsMigrated:F?d.glowRooms:0,canonicalized:F?T:0,wallSegmentsMigrated:F?I:0,legacyZeroWallsMigrated:F?$:0,wallsMerged:F?x:0,spansMerged:F?S:0,partitionsMerged:F?M:0,partitionsReconciled:F?C:0,openingsRehosted:F?D:0,redundantDraftsRemoved:F?P:0,wallsStraightened:F?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:F?v:0,maxStraightenSpace:F?y:"",latticeCoordinatesCanonicalized:F?r.canonicalized:0,latticeCoordinatesFar:F?r.far:0,latticeMaxShift:F?r.maxShift:0,latticeMaxShiftCm:F?r.maxShiftCm:0,latticeSpaces:F?r.spaces:[],...O},changed:F}}function ug(e,t,i){const s=(e?.spaces||[]).find(e=>e?.id===i),n=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),o=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===i||"string"==typeof e.room_id&&n.has(e.room_id)||t?.[e.id]?.s===i).map(e=>e.id))].sort((e,t)=>e.localeCompare(t));return{markerIds:o,count:o.length}}function _g(e){return e instanceof Error?e.name||"Error":typeof e}function mg(e){return Md({id:e?.id??"",cell_cm:e?.cell_cm,rooms:e?.rooms||[],walls:e?.walls||[],wall_segments:e?.wall_segments||[],open_spans:e?.open_spans||[],openings:e?.openings||[],partitions:e?.partitions||[],room_drafts:e?.room_drafts||[],wall_columns:e?.wall_columns||[]})}function gg(e,t,i,s=zh,n=1e3){return(Array.isArray(e?.openings)?e.openings:[]).flatMap(e=>{const o={...e,rx:Number(e.x)*n,ry:Number(e.y)*n,rlen:Number(e.length)*n};if(!e.host||"wall"===e.host.kind)return[o];const a=Tp(e,t.partitions,n,i,s);return a.resolved?[{...e,rx:a.resolved.center[0],ry:a.resolved.center[1],rlen:a.resolved.length,angle:a.resolved.angle,partitionHost:a.resolved}]:[]})}function fg(e,t=()=>!0){return e.flatMap(e=>e.host&&e.partitionHost&&t(e)?[Ap(e.partitionHost)]:[])}function vg(e,t,i,s,n,o,a=zh,r=1e3){const l=gc(t.rooms,[...i],s,n,o,a,r);return e.flatMap(e=>{const t={x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen};return e.host&&"wall"!==e.host.kind?e.partitionHost&&Ep(e.partitionHost,l,2e-4*a)?[t]:[]:[t]})}function yg(e,t){const i=Fh,s=zh,n=$h,o=Number.isFinite(Number(t.cellCm))&&Number(t.cellCm)>0?Number(t.cellCm):5,a=Array.isArray(e?.walls)?e.walls:[],r=function(e,t,i=zh,s=1e3){return yd(t.rooms,e?.open_spans,s,.02*i)}(e,t,s,n),l=function(e,t,i=zh){if(!t.length)return[];const s=e.filter(e=>e?.id),n=.02*i,o=[];for(let e=0;e{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return h.some(e=>Hs(t,e)<4*n)}).map(e=>[...e]);d.length&&o.push({a:a,b:r,segs:d})}return o}(t.rooms,r,s).flatMap(e=>e.segs),c=gg(e,t,o,s,n),h=fg(c),d=rh(t,o,s,2e-4*s,h).all;return{space:t,walls:a,openCuts:l,openings:c,roomOpenings:vg(c,t,a,l,i,o,s,n),partitionCuts:h,physicalBodies:d,wallKeyPitch:i,cellCm:o,gridPitch:s,coordScale:n}}function bg(e,t,i){const s="string"==typeof e?.title?e.title.trim():"",n=null==e?.id?"":String(e.id).trim();return{spaceId:n,displayName:s||n||i(t+1)}}function wg(e,t={}){const i=(t.fingerprint||Md)(e),s=Array.isArray(e?.spaces)?e.spaces:[],n=t.fallbackSpaceName||(e=>`Space ${e}`),o=t.prepareSpace||yg,a=t.wallPass||Ec,r=t.floorPass||zc,l=[];for(let i=0;i0||d.physicalBodies.length>0;if(!d.space.rooms.length&&!p){l.push({...h,status:"not-applicable"});continue}let u=null;if(p){try{u=a(d.space.rooms,d.walls,d.openCuts,d.roomOpenings,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale,d.physicalBodies)}catch(e){l.push({...h,status:"failed",reason:"wall-exception",detail:_g(e)});continue}if(null==u){l.push({...h,status:"failed",reason:"wall-null"});continue}if("degraded-extra"===u.status){l.push({...h,status:"failed",reason:"wall-degraded-extra"});continue}if("failed-core"===u.status){l.push({...h,status:"failed",reason:"wall-failed-core"});continue}t.captureWallGeometry?.(d,u)}if(d.space.rooms.length&&null==u?.paperGeom){let e;try{e=r(d.space.rooms,d.walls,d.openCuts,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale)}catch(e){l.push({...h,status:"failed",reason:"floor-exception",detail:_g(e)});continue}if(null==e){l.push({...h,status:"failed",reason:"floor-null"});continue}}l.push({...h,status:"ok"})}const c=l.filter(e=>"failed"===e.status);return{fingerprint:i,spaces:l,failures:c,ok:0===c.length}}class kg{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}clear(){this._undo=[],this._redo=[]}}const xg=new WeakMap;const $g=(e,t,i)=>255===e[i+3]&&t.every((t,s)=>Math.abs(e[i+s]-t)<=2);async function Sg(e){const t=e.defaultView;if(!t||!t.CSS?.supports?.("mix-blend-mode","screen"))return!1;const i=[128,32,16],s=[16,64,128],n=[9,19,29],o=`\n \n \n \n \n \n `,a=t.URL.createObjectURL(new Blob([o],{type:"image/svg+xml"}));try{const o=await new Promise((e,i)=>{const s=new t.Image,n=t.setTimeout(()=>i(new Error("SVG blend probe timeout")),1e3);s.onload=()=>{t.clearTimeout(n),e(s)},s.onerror=()=>{t.clearTimeout(n),i(new Error("SVG blend probe failed"))},s.src=a}),r=e.createElement("canvas");r.width=4,r.height=1;const l=r.getContext("2d",{willReadFrequently:!0});if(!l)return!1;l.drawImage(o,0,0,4,1);const c=l.getImageData(0,0,4,1).data;return $g(c,i,0)&&$g(c,function(e,t){return[0,1,2].map(i=>Math.round(255-(255-e[i])*(255-t[i])/255))}(i,s),4)&&$g(c,s,8)&&$g(c,n,12)}catch{return!1}finally{t.URL.revokeObjectURL(a)}}const Mg={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},Cg=e=>Math.max(0,Math.min(1,e)),Dg=(e,t,i)=>e+(t-e)*i,Pg=(e,t,i)=>{const s=1-e;return 3*s*s*e*t+3*s*e*e*i+e*e*e};function Tg(e,t){return{centerX:e.x+e.w/2,centerY:e.y+e.h/2,pixelsPerUnit:t>0&&e.w>0?t/e.w:1,viewBox:{...e}}}function Ig(e,t,i){const s=Math.max(1e-9,e.pixelsPerUnit),n=Math.max(1e-9,t/s),o=Math.max(1e-9,i/s);return{x:e.centerX-n/2,y:e.centerY-o/2,w:n,h:o}}function Rg(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 s=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return s?[parseInt(s.slice(0,2),16),parseInt(s.slice(2,4),16),parseInt(s.slice(4,6),16),8===s.length?parseInt(s.slice(6,8),16)/255:1]:null}function Ag(e,t,i){const s=Rg(e),n=Rg(t);if(!s||!n)return i<.5?e:t;const o=Cg(i);return`rgba(${Math.round(Dg(s[0],n[0],o))}, ${Math.round(Dg(s[1],n[1],o))}, ${Math.round(Dg(s[2],n[2],o))}, ${Dg(s[3],n[3],o).toFixed(3)})`}function zg(e,t,i){const s=(e=>{const t=Cg(e);if(0===t||1===t)return t;let i=0,s=1,n=t;for(let e=0;e<12;e++)n=(i+s)/2,Pg(n,.2,.2){const t=this._state;if(!t||t.token!==n)return;const i=Cg((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:zg(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(a):(this._raf=0,this._hooks.settled(t),this._state?.token===n&&(this._state=null))};return this._raf=this._clock.requestFrame(a),n}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 Eg="houseplan_card_labs_v1",Ng=Object.freeze([Object.freeze({id:"iso",issue:89,since:"1.62.0",expires:"1.65.0",summary:"Volumetric plan renderer"})]);function Og(e){const t=/^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(String(e||"").trim());if(!t)return null;const i=t.slice(1).map(Number);return i.every(e=>Number.isSafeInteger(e))?i:null}function Hg(e,t){for(let i=0;i<3;i++)if(e[i]!==t[i])return e[i]e.split(",")).map(e=>e.trim()).filter(Boolean)}function Ug(e,t,i,s=Ng){const n=function(e,t=Ng){const i=Og(e),s=new Map;if(!i||!Bg(t))return s;for(const e of t){const t=Og(e.since),n=Og(e.expires);Hg(i,t)>=0&&Hg(i,n)<0&&s.set(e.id,e)}return s}(i,s),o=new Set(Bg(s)?s.map(e=>e.id):[]),a=new Set(function(e){if(!e)return[];try{const t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}catch{return[]}}(t).filter(e=>n.has(e)));let r=!1;const l=e=>{if("off"===e)return a.clear(),void(r=!0);const t=e.startsWith("-"),i=t?e.slice(1):e;o.has(i)&&(r=!0,t||!n.has(i)?a.delete(i):a.add(i))};for(const t of jg(new URLSearchParams(String(e.search||"").replace(/^\?/,""))))l(t);for(const t of jg(qg(e.hash)))l(t);const c=Object.freeze([...a].sort());return{active:c,space:Wg(e.hash),persist:r?JSON.stringify(c):void 0,knownUrlOperation:r}}let Gg={active:Object.freeze([]),space:""},Vg="",Kg=!1,Yg="";const Jg=new Set;function Xg(){if("undefined"==typeof window)return Gg;const e=Ug(window.location,function(){try{return window.localStorage.getItem(Eg)}catch{return null}}(),Vg);void 0!==e.persist&&function(e){try{window.localStorage.setItem(Eg,e)}catch{}}(e.persist),Gg={active:e.active,space:e.space},window.__hpLabs=e.active;for(const e of Jg)e(Gg);return Gg}function Zg(){Xg()}function Qg(e){return Vg=e,"undefined"==typeof window?Gg:(Kg||(Kg=!0,window.addEventListener("hashchange",Zg),window.addEventListener("popstate",Zg)),Xg())}const ef=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])});function tf(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 sf(e,t,i=ef){if(!(tf(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const s=i.rotDeg*Math.PI/180,n=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],a=e[1]-i.origin[1],r=(o*Math.cos(s)-a*Math.sin(s))*i.xyScale,l=(o*Math.sin(s)+a*Math.cos(s))*i.xyScale;return[i.origin[0]+r,i.origin[1]+l*Math.cos(n)-t*i.zScale*Math.sin(n)]}function nf(e,t=ef){if(!tf(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,s=t.tiltDeg*Math.PI/180,n=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(s));return[t.origin[0]+n*Math.cos(i)+o*Math.sin(i),t.origin[1]-n*Math.sin(i)+o*Math.cos(i)]}function of(e=ef){return`matrix(${function(e=ef){if(!tf(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,s=e.xyScale*Math.cos(t),n=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),a=e.xyScale*Math.cos(t)*Math.cos(i);return[s,o,n,a,e.origin[0]-s*e.origin[0]-n*e.origin[1],e.origin[1]-o*e.origin[0]-a*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function af(e,t=ef){const{rect:i,wallHeight:s}=e,n=e.openingHeight??s,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(s)||!Number.isFinite(n)||!Number.isFinite(o)||s<0||n<0||o<0)throw new Error("invalid isometric frame");const a=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],r=Math.max(s,n),l=a.flatMap(e=>[sf(e,-o,t),sf(e,r,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),p=Math.min(...h);return{x:d,y:p,w:Math.max(...c)-d,h:Math.max(...h)-p}}const rf=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function lf(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 s=!t;return lf(i)>0===s?i:[...i].reverse()}function hf(e,t,i){return e.length?`M ${e.map(e=>rf(sf(e,i,t))).join(" L ")} Z`:""}function df(e,t,i){const s=t[1]-e[1],n=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return s*Math.sin(o)+n*Math.cos(o)}function pf(e,t,i=ef){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const s=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=cf(e,t);if(!i.length)return i;let s=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(lf(e))>=1e-9)).filter(e=>e.length&&lf(e[0])>0);s.sort((e,t)=>{const i=e[0][0],s=t[0][0];return i[0]-s[0]||i[1]-s[1]||Math.abs(lf(t[0]))-Math.abs(lf(e[0]))});const n=[],o=[];let a=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:n.join(" "),sides:o,componentCount:s.length,edgeCount:a}}function uf(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const _f=e=>e.every(Number.isFinite);function mf(e,t,i,s){const n=e[0]*i,o=e[1]*s,a=t*Math.PI/180;return[n*Math.cos(a)-o*Math.sin(a),n*Math.sin(a)+o*Math.cos(a)]}function gf(e,t,i,s,n,o,a){const r=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],mf(i,e.angle,r,l));return{leaf:t,hinge:h,closedVector:mf(s,e.angle,r,l),quarterVector:mf([-s[1],s[0]],e.angle,r,l),turnDeg:n,bottom:o,top:a}}const ff=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function vf(e,t,i=ef){const s=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const n=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,s),o=sf(t.hinge,t.bottom,i),a=sf(n,t.bottom,i),r=sf(n,t.top,i),l=sf(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${ff(o)} L ${ff(a)} L ${ff(r)} L ${ff(l)} Z`,shadowD:`M ${ff(sf(t.hinge,0,i))} L ${ff(sf(n,0,i))}`,depth:Math.max(o[1],a[1])}})}const yf={window:120,door:90,passage:90,gate:300};function bf(e){return yf[e]}function wf(e,t){const i=[e[0],e[1]],s=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,s)<=0?{a:i,b:s}:{a:s,b:i}}function kf(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const s=wf(e.a,e.b);if(Math.hypot(s.b[0]-s.a[0],s.b[1]-s.a[1])<=1e-9)return;const n=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])}`}(s.a,s.b),o=t.get(n);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(n,{segmentKey:n,a:s.a,b:s.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function xf(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 $f(e){const t=kf(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],s=t.b[1]-t.a[1],n=Math.hypot(i,s),o=i/n,a=s/n,r=(e[0]-t.a[0])*o+(e[1]-t.a[1])*a,l=Math.max(0,Math.min(n,r)),c=t.a[0]+l*o,h=t.a[1]+l*a;return{along:l,length:n,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*a-(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,s,n){const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],r=Math.hypot(o,a);if(!(r>1e-9))return!1;const l=o/r,c=a/r;for(const o of i){if(!o.open&&o.kind)continue;const i=wf(o.a,o.b),a=i.b[0]-i.a[0],r=i.b[1]-i.a[1],h=Math.hypot(a,r);if(!(h>1e-9))continue;const d=a/h,p=r/h;if(Math.abs(l*p-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>n)continue;const u=(e[0]-i.a[0])*d+(e[1]-i.a[1])*p,_=1e-9;if(!(u<=_||u>=h-_)&&Math.abs((e[0]-i.a[0])*p-(e[1]-i.a[1])*d)<=s+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(xf),s=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort(xf);let n=s[0];if(!n)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=s.filter(e=>Math.abs(e.distance-n.distance)<=1e-9&&Math.abs(e.perpendicular-n.perpendicular)<=1e-9),a=o.filter(e=>e.target.partitionHost);if(a.length){if(1!==new Set(a.map(e=>e.target.partitionHost.id)).size||a.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=a[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],s=Math.hypot(t,i),r=t/s,l=i/s,c=o.every(t=>{const i=t.target.b[0]-t.target.a[0],s=t.target.b[1]-t.target.a[1],n=Math.hypot(i,s),o=i/n,a=s/n,c=Math.abs(r*a-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*r);return c&&h<=1e-9});if(!c)return{candidate:null,jambBlockedTarget:null};n=e}if(n.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:r,length:l}=n,c=r.b[0]-r.a[0],h=r.b[1]-r.a[1],d=c/l,p=h/l,u=r.partitionHost?r.physicalHalfWidth:0,_=Math.min(Math.max(0,e.renderedLength)/2+u,l/2);let m=n.along;const g=Math.max(e.gridStep,1e-9),f=l/2;m=Math.abs(m-f)<=g/2?f:Math.round(m/g)*g,m=Math.max(_,Math.min(l-_,m));const v=r.a[0]+d*m,y=r.a[1]+p*m,b=Math.max(0,e.renderedLength)/2,w=m-b,k=m+b,x=Math.max(0,w),$=Math.max(0,l-k),S=[r.a[0]+d*(w-x/2),r.a[1]+p*(w-x/2)],M=[r.a[0]+d*(k+$/2),r.a[1]+p*(k+$/2)];let C=180*Math.atan2(h,c)/Math.PI;C>=90?C-=180:C<-90&&(C+=180);const D=Math.abs(m-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:r,...r.partitionHost?{host:{...r.partitionHost,t:l>0?m/l:0}}:{},measure:{labels:[{distance:x,midpoint:S},{distance:$,midpoint:M}],guide:D?{x:v,y:y,angle:C}:null}},jambBlockedTarget:null}}function Sf(e,t,i,s,n=1e-6){return e.presetRevision===i&&e.geometryRevision===s&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=n}const Mf=(e,t)=>e[0]*t[1]-e[1]*t[0],Cf=(e,t)=>e[0]*t[0]+e[1]*t[1],Df=(e,t)=>[e[0]-t[0],e[1]-t[1]],Pf=(e,t,i)=>[e[0]+t[0]*i,e[1]+t[1]*i];function Tf(e,t,i){return Math.abs(Mf(e,t))<=i}function If(e,t,i){return Math.abs(Mf(Df(e,t),i))}function Rf(e,t,i,s,n){const o=t[0]-e[0],a=t[1]-e[1],r=Math.hypot(o,a);if(!(r>n&&i>0))return[];const l=[o/r,a/r];return function(e,t,i){const s=t.map(([t,i])=>[Math.max(0,Math.min(t,i)),Math.min(e,Math.max(t,i))]).filter(([e,t])=>t>e+i).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),n=[];for(const e of s){const t=n[n.length-1];!t||e[0]>t[1]+i?n.push([...e]):t[1]=Math.max(t[1],e[1])}const o=[];let a=0;for(const[e,t]of n)e>a+i&&o.push([a,e]),a=Math.max(a,t);return e>a+i&&o.push([a,e]),o}(r,s.flatMap(t=>{const s=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(s,o);if(!(a>n&&Tf(l,[s/a,o/a],1e-6)))return[];if(If(t.a,e,l)>1.1*Math.max(i,n)||If(t.b,e,l)>1.1*Math.max(i,n))return[];return[[Cf(Df(t.a,e),l),Cf(Df(t.b,e),l)]]}),n).flatMap(([t,s])=>{const n=Wl({a:Pf(e,l,t),b:Pf(e,l,s),halfDepth:i});return n?[n]:[]})}function Af(e){const t=e.coordScale??1,i=Math.max(e.epsilon??2e-4*e.gridPitch,1e-9),s=[];for(const i of e.rooms||[]){if(!i?.id)continue;const n=mc(e.rooms,i.id,e.walls,e.openCuts,e.pitch,e.cellCm,e.gridPitch,t);if(!n||n.poly.length<3)continue;const o=sc(n.poly,n.offsets);!o||o.length<3||s.push({roomId:i.id,profile:n,inner:o})}s.sort((e,t)=>e.roomId.localeCompare(t.roomId));const n=(e.roomOpenings||[]).map(e=>{const t=e.angle*Math.PI/180,i=[Math.cos(t),Math.sin(t)],s=Math.max(0,e.length)/2;return{a:Pf([e.x,e.y],i,-s),b:Pf([e.x,e.y],i,s)}}),o=[];for(const s of Hc(e.rooms,e.walls,e.openCuts,e.pitch,e.cellCm,e.gridPitch,t))for(const e of Rf(s.a,s.b,s.depthUnits/2,n,i))o.push({body:e});const a=new Map;for(const t of e.partitionCuts||[]){const e=a.get(t.hostId)||[];e.push(t),a.set(t.hostId,e)}for(const t of e.partitions||[]){const s=wl(t.cm,e.cellCm,e.gridPitch)/2;for(const e of Rf(t.a,t.b,s,a.get(t.id)||[],i))o.push({body:e,partitionId:t.id})}return{rooms:s,boundaries:o,epsilon:i}}function zf(e,t,i,s){const n=e.profile.offsets[t];if(!(n>0))return null;const o=function(e,t){const i=e[t],s=e[(t+1)%e.length],n=s[0]-i[0],o=s[1]-i[1],a=Math.hypot(n,o)||1;return function(e){let t=0;for(let i=0;i=0?[-o/a,n/a]:[o/a,-n/a]}(e.profile.poly,t),a=Pf(i.center,o,n),r=[];for(let t=0;ts&&Tf(i.axis,[l[0]/c,l[1]/c],1e-6)))continue;if(If(n,a,i.axis)>s||If(o,a,i.axis)>s)continue;const h=Cf(Df(n,a),i.axis),d=Cf(Df(o,a),i.axis);r.push([Math.min(h,d),Math.max(h,d)])}const l=function(e,t){e.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);const i=[];for(const s of e){const e=i[i.length-1];!e||s[0]>e[1]+t?i.push([...s]):e[1]=Math.max(e[1],s[1])}return i}(r,s).find(([e,t])=>e<=s&&t>=-s);if(!l)return null;const[c,h]=l,d=Pf(a,i.axis,-i.half),p=Pf(a,i.axis,i.half),u=Math.max(0,-i.half-c),_=Math.max(0,h-i.half),m=u>0?Pf(a,i.axis,c):d,g=_>0?Pf(a,i.axis,h):p,f=Cf(o,i.normal)>=0?1:-1,v=(t,s,n)=>({from:t,to:s,label:[(t[0]+s[0])/2,(t[1]+s[1])/2],axis:i.axis,labelNormal:o,distance:n,roomId:e.roomId,roomSide:f,source:"room-face"});return{left:v(d,m,u),right:v(p,g,_)}}function Ff(e){const t=Pf(e.center,e.axis,-e.half),i=Pf(e.center,e.axis,e.half),s=Pf(e.center,e.axis,Math.min(-e.half,e.targetLo)),n=Pf(e.center,e.axis,Math.max(e.half,e.targetHi)),o=(t,i)=>({from:t,to:i,label:[(t[0]+i[0])/2,(t[1]+i[1])/2],axis:e.axis,labelNormal:e.normal,distance:Math.hypot(i[0]-t[0],i[1]-t[1]),source:"host-end"});return[o(t,s),o(i,n)]}function Ef(e,t,i,s){const n=Df(i,t),o=Cf(n,n);if(!(o>s*s))return Math.hypot(...Df(e,t))<=s;const a=Cf(Df(e,t),n)/o;if(a<-s||a>1+s)return!1;const r=Pf(t,n,Math.max(0,Math.min(1,a)));return Math.hypot(...Df(e,r))<=s}function Nf(e,t,i){let s=!1;for(let n=0,o=t.length-1;ne[1]!=c>e[1]&&e[0]<(l-a)*(e[1]-r)/(c-r)+a&&(s=!s)}return s}function Of(e,t,i,s,n,o){const a=Df(s,i),r=Mf(t,a);if(Math.abs(r)<=o)return null;const l=Df(i,e),c=Mf(l,a)/r,h=Mf(l,t)/r;return c<-o||c>n+o||h<-o||h>1+o?null:Math.max(0,Math.min(n,c))}function Hf(e,t,i){const s=Ff(i),n="partition"===e.host?.kind?e.host.id:void 0;return[{base:s[0],direction:[-i.axis[0],-i.axis[1]]},{base:s[1],direction:i.axis}].map(({base:e,direction:i})=>{const s=e.distance,o=function(e,t,i,s,n,o){let a=null;for(const r of s)if(!n||r.partitionId!==n){if(Nf(e,r.body,o))return 0;for(let s=0;sCf(Df(e,n),i));return{center:n,axis:i,normal:s,half:Math.max(0,e.renderedLength)/2,targetLo:Math.min(...o),targetHi:Math.max(...o)}}(e),s=t.rooms.flatMap(e=>{const s=function(e,t,i){const{profile:s}=e;let n=null;for(let e=0;e0))continue;const o=s.poly[e],a=s.poly[(e+1)%s.poly.length],r=Df(a,o),l=Math.hypot(r[0],r[1]);if(!(l>i&&Tf(t.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(If(o,t.center,t.axis)>i||If(a,t.center,t.axis)>i)continue;const c=Cf(Df(o,t.center),t.axis),h=Cf(Df(a,t.center),t.axis),d=Math.min(c,h),p=Math.max(c,h);if(d>-t.half+i||p0?d:p<0?-p:0);(!n||u2)return Ff(i);if(s.length){const e=s.map(({room:e,edge:s})=>zf(e,s,i,t.epsilon));if(e.some(e=>!e))return Ff(i);const n=e;return[...n.map(e=>e.left),...n.map(e=>e.right)]}return Hf(e,t,i)}function Bf(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}const qf="1.68.1",Wf={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},jf={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},Uf=2e3,Gf=1e3,Vf=1500,Kf=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},Yf=(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}},Jf=(e,t,i,s)=>{for(e.delete(t),e.set(t,i);e.size>s;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},Xf=new Map;let Zf=0;const Qf=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`;let ev=1e4;const tv="houseplan_card_layout_v1",iv="houseplan_card_cfg_v1",sv="houseplan_card_zoom_v1",nv="houseplan_card_nav_v1",ov="houseplan_card_kiosk_v1",av="houseplan_card_view_v1",rv="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",lv=1e3,cv=[[0,1],[45,.88],[70,.62],[86,.32],[100,0]],hv=(e,t)=>{const i=Math.min(e.x,t.x),s=Math.min(e.y,t.y);return{x:i,y:s,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-s}},dv=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),pv=2e3,uv=(e,t,i)=>{const s=new Event(t,{bubbles:!0,composed:!0});s.detail=i??{},e.dispatchEvent(s)},_v=e=>{history.pushState(null,"",e),uv(window,"location-changed",{replace:!1})},mv=(e,t)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const t=s;s=null,t&&e(...t)},t)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=s;s=null,t&&e(...t)},n.cancel=()=>{clearTimeout(i),i=void 0,s=null},n.pending=()=>void 0!==i,n},gv=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}};class fv extends he{constructor(){super(...arguments),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=mn(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._pointerModality=new q_(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Er},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._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._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 Fg({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._editorSecondary=new K_({root:()=>this.renderRoot,requestUpdate:()=>this.requestUpdate(),updateComplete:()=>this.updateComplete,clearTip:()=>{this._tip=null}}),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 kg(50),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._glowClipCache=new Map,this._glowGeometryWarnings=new Set,this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowFeatherUnits=null,this._glowRenderedSources=new Map,this._glowLastAppearance=new Map,this._glowEnteringSources=new Set,this._glowEnterRafs=new Map,this._glowFadeTimers=new Map,this._glowFeatherSuspendUntil=0,this._glowFeatherResumeTimer=0,this._glowSourceSeq=0,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._resize=new rr,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._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._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._alignDialog=null,this._settingsDialog=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._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=++Zf,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={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._clearTransientHover(!0),this._cancelDevicePressFeedback(),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+N_)>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._lastTap=0,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._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._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=An(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,function(e){const t=Cn(e);t&&(Tn(e,t),In(e,t))}(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 Pd(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=mv(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=hl(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._layout=dl(Xr(this._layout)),localStorage.setItem(tv,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._resize.dragging)return void this._rszCancelDrag();const e=this._geometryHistory.undo();e&&(this._applyGeometryState(e.before,!0)?this._showToast(this._t("history.undone",{name:e.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._resize.dragging)return void this._rszCancelDrag();const e=this._geometryHistory.redo();e&&(this._applyGeometryState(e.after,!0)?this._showToast(this._t("history.redone",{name:e.name})):this._geometryHistory.clear())},this._saveConfigDebounced=mv(()=>{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=()=>{const e=this._physicalDialog,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=Kf(e.cm);if(null==s)return void this._showPhysicalRange("column"===e.kind?Yc:100,"column"===e.kind?1:0);const n=this._imperial?2.54*s:s,o="column"===e.kind?Yc:100,a="column"===e.kind?1:0;if(!Number.isFinite(n)||no)return void this._showPhysicalRange(o,a);if("column"===e.kind){const t=i.wall_columns.find(t=>t.id===e.id);if(!t)return;const s=Kf(e.angle||"0");if("circle"!==e.shape&&(null==s||s<0||s>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===e.shape?{id:e.id,shape:"circle",center:t.center,cm:n}:{id:e.id,shape:"square",center:t.center,cm:n,angle:s};if(i.wall_columns.some(t=>t.id!==e.id&&vh(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===e.kind){const i=(t.partitions||[]).find(t=>t.id===e.id);if(i&&0===n&&k_(t.openings,{kind:"partition",id:e.id}))return void this._showToast(this._t("toast.zero_wall_opening_conflict"));i&&(i.cm=n)}else if("column"===e.kind){const i=(t.wall_columns||[]).find(t=>t.id===e.id);i&&(i.cm=Xc(n),i.shape="circle"===e.shape?"circle":"square","square"===i.shape?i.angle=Kf(e.angle||"0"):delete i.angle)}else{const i=(t.room_drafts||[]).find(t=>t.id===e.id);i?.segments?.[e.segment||0]&&(i.segments[e.segment||0].cm=n)}const l=this._commitPhysicalGeometry(this._t("history.physical_edit"),r);this._physicalDialog=null,l||this.requestUpdate()},this._deletePhysicalSelection=()=>{const e=this._physicalSel,t=this._curSpaceCfg;if(!e||!t)return;if("draft"===e.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot();if("partition"===e.kind){const i=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===e.id).sort((e,t)=>(e.host?.t||0)-(t.host?.t||0));if(i.length)return void(this._partitionDeleteDialog={id:e.id,openings:i.map(e=>JSON.parse(JSON.stringify(e)))})}const s="partition"===e.kind?"partitions":"column"===e.kind?"wall_columns":"room_drafts";t[s]=(t[s]||[]).filter(t=>t.id!==e.id),t[s].length||delete t[s],this._activeDraftId===e.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._confirmPartitionDelete=()=>{const e=this._partitionDeleteDialog,t=this._curSpaceCfg;if(!e||!t)return;const i=this._geometrySnapshot();t.partitions=(t.partitions||[]).filter(t=>t.id!==e.id),t.partitions.length||delete t.partitions,t.openings=(t.openings||[]).filter(t=>"partition"!==t.host?.kind||t.host.id!==e.id),t.openings.length||delete t.openings,this._partitionDeleteDialog=null,this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._deleteDraftWhole=()=>{const e="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,t=this._curSpaceCfg;if(!e||!t||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();t.room_drafts=(t.room_drafts||[]).filter(t=>t.id!==e),t.room_drafts.length||delete t.room_drafts,this._activeDraftId===e&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._deleteDraftSegment=()=>{const e=this._physicalDialog,t=this._curSpaceCfg;if(!e||"draft"!==e.kind||!t)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(t.room_drafts||[]).findIndex(t=>t.id===e.id);if(i<0)return;const s=t.room_drafts[i],n=Math.max(0,Math.min(s.segments.length-1,e.segment||0)),o=[],a=s.points.slice(0,n+1),r=s.points.slice(n+1);if(a.length>=2&&o.push({id:s.id,points:a,segments:s.segments.slice(0,n)}),r.length>=2&&o.push({id:o.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:r,segments:s.segments.slice(n+1)}),2===o.length&&t.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();t.room_drafts.splice(i,1,...o),t.room_drafts.length||delete t.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=o.length?{kind:"draft",id:o[0].id}:null,this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),l)},this._confirmRoomDelete=e=>{const t=this._roomDeleteDialog,i=this._curSpaceCfg,s=this._spaceModel();if(!t||!i||!s)return;const n=s.rooms.find(e=>e.id===t.roomId);if(!n)return void(this._roomDeleteDialog=null);const o=this._openCuts(),a=Pu(gc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,lv).filter(e=>e.roomId===n.id),s.partitions,this._openingsR.map(e=>({...e,x:e.rx,y:e.ry})),.02*this._gridPitch),r=new Set(a.materialize.filter(e=>!e.reusePartitionId).map(e=>e.interval.key)).size;if(e&&(i.partitions||[]).length+r>pv)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot(),c=fc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,lv);if(e){i.partitions||=[];const e=new Map,t=new Map,s=e=>{const t=(i.wall_segments||[]).find(t=>{const i=[Number(t.a?.[0])*lv,Number(t.a?.[1])*lv],s=[Number(t.b?.[0])*lv,Number(t.b?.[1])*lv];return this._samePt(e.a,i)&&this._samePt(e.b,s)||this._samePt(e.a,s)&&this._samePt(e.b,i)});return"string"==typeof t?.id&&t.id?t.id:null},n=Date.now().toString(36);for(const o of a.materialize){let a=o.reusePartitionId?i.partitions.find(e=>e.id===o.reusePartitionId):t.get(o.interval.key);a||(a={id:s(o.interval)||`partition-room-${n}-${t.size}`,a:[o.interval.a[0]/lv,o.interval.a[1]/lv],b:[o.interval.b[0]/lv,o.interval.b[1]/lv],cm:o.interval.cm},i.partitions.push(a),t.set(o.interval.key,a)),e.set(o.interval.key,a)}for(const t of i.openings||[]){const i=a.openingIntervals.get(t.id),s=i?e.get(i):null,n=this._openingsR.find(e=>e.id===t.id);if(!s||!n)continue;const o={a:[s.a[0]*lv,s.a[1]*lv],b:[s.b[0]*lv,s.b[1]*lv]};t.host={kind:"partition",id:s.id,t:Tu([n.rx,n.ry],o)}}}else if(a.removeOpeningIds.length){const e=new Set(a.removeOpeningIds);i.openings=(i.openings||[]).filter(t=>!e.has(t.id)),i.openings.length||delete i.openings}i.rooms=i.rooms.filter(e=>e.id!==n.id),this._cfgEpoch++;const h=this._normalizeWalls(c,this._openCuts());h.length?i.walls=h:delete i.walls,this._roomDeleteDialog=null,this._commitPhysicalGeometry(this._t(e?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()},this._rebindPartitionOpening=()=>{const e=this._openingDialog;if(!e?.id)return;const t=e.id;this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingRebindId=t,this._openingPreset={type:e.type,lengthCm:e.lengthCm,flipH:e.flipH,flipV:e.flipV,revision:++this._openingPresetRevision},this._openingDialog=null,this._openingHoverCandidate=null)},this._keepClosedAsPartitions=()=>{if(this._wallFaceBatch)return void this._decideWallFace(!1);if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const e=this._curSpaceCfg,t=this._path.slice(0,-1);if((e.partitions||[]).length+t.length>pv)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=iu(t.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul);e.partitions||=[];const n=Date.now().toString(36),o=this._activeDraftId?(e.room_drafts||[]).find(e=>e.id===this._activeDraftId):null,a=this._draftSegmentsForPath(this._path,o,s);for(let i=0;ie.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),this._commitPhysicalGeometry(this._t("history.contour_to_partitions"),i),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const e=this._spaceDialog;if(e)if(e.pickSaved)this._spaceDialog={...e,pickSaved:!1};else{this._spaceDialog={...e,pickSaved:!0,savedBusy:!0};try{const e=await this.hass.callWS({type:"houseplan/plans/list"}),t=this._spaceDialog;t&&(this._spaceDialog={...t,saved:e?.plans||[],savedBusy:!1})}catch(e){const t=this._spaceDialog;t&&(this._spaceDialog={...t,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(e)}))}}},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=br(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const e=this._glowRadiusCm,t=this._imperial?Math.round(e/30.48*10)/10:Math.round(e)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:t,bgColor:hs(this._settings,{bgColor:null})||null,northDeg:Pr(this._settings,{}),bgMode:Tr(this._settings,{}),sunRays:Ir(this._settings,{}),busy:!1}},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const e=this._alignDialog;e&&!e.busy&&e.report.liveMissingPositions.length&&this._previewAlignDialog(!e.removeLiveMissingPositions)},this._optimizeUndoBusy=!1,this._openBackupExport=()=>{this._settingsDialog=null,this._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._openRulesDialog=()=>{if(!this._norm)return;const e=this._settings.icon_rules,t=(e&&e.length?e:Je).map(e=>({...e}));this._rulesDialog={rules:t,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}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}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const s=document.createElement("span");s.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(s);const n=getComputedStyle(s).color||t;return s.remove(),n}_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 s=this.renderRoot.querySelector(".editorchrome"),n=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),a=this.renderRoot.querySelector(".hp-backdrop"),r=this.renderRoot.querySelector(".zoomwrap"),l=r?getComputedStyle(r).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:s?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:Tg(n,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:a&&Number(getComputedStyle(a).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,s,n){const o=Ni(this._baseVb(),s/Math.max(1,n)),a=Math.min(fv.ZOOM_MAX,Math.max(fv.ZOOM_MIN,e)),r=o.w/a,l=o.h/a,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-r/2,y:h-l/2,w:r,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 s=this.renderRoot.activeElement;(!s||!s.isConnected||!!s.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,s){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const n=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,s,n,o),this._lastValidStageSize=[n,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,s,n,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 a=this.renderRoot.querySelector(".editorchrome"),r=a?.querySelector(".editorchrome-inner"),l="view"===i?0:r?.scrollHeight||r?.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(s,n,o),void this.requestUpdate();const p=this._viewForModeTarget(s,n,o,d,h),u={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:Tg(p,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 _=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,u,i,this._reducedMotion||_?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,s=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?s:"number"==typeof i?"valid"===s.kind||"invalid"===s.kind&&"out-of-range-index"===s.reason?{kind:"pending",value:i}:s:"valid"===s.kind||"invalid"===s.kind&&"unknown-id"!==s.reason?s:{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._clearTransientHover(!0),this._cancelDevicePressFeedback()),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 Bf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){Bf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(gv(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 s=this._model.map(e=>e.id),n=s.indexOf(i);if(n<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const a of o){const o=a.dataset.id||"";if(!o||o===i)continue;const r=a.getBoundingClientRect();if(er.right||tr.bottom)continue;const l=s.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,s;const n=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===n?.targetId&&t.placement===n?.placement||(this._tabDrag={...t,moved:!0,targetId:n?.targetId||null,placement:n?.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 s=this._model.map(e=>e.id),n=function(e,t,i){const s=e.indexOf(t),n=e.indexOf(i);if(s<0||n<0||s===n)return e;const o=e.slice();return o.splice(s,1),o.splice(n,0,t),o}(s,e,t);if(n===s)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),a=function(e,t,i,s=()=>""){if(!i)return[];const n=[];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 a=("string"==typeof o.area?o.area:"")||s(e)||"";a&&t[a]||n.push({id:e,space:i})}return n}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),s[0]||"",e=>o.get(e)||"");if(a.length){const e=new Map(a.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||[],n),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),s=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&s0&&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()}_newContinuityController(){return new Sd(()=>{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(av,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],s="iso"===e?nf(i):i;return{x:s[0],y:s[1]}}_convertProjectionView(e,t){if(e===t)return;const i=this._logicalViewCenter(e);this._view=null;const s=i?"iso"===t?sf([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,s?.[0],s?.[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 Wg(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=(t=this.ownerDocument,xg.get(t)?.resolved);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=xd(this.ownerDocument,this._pageVisibility),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),function(e){const t=xg.get(e);if(t)return t.promise;const i={promise:Promise.resolve(!1)};return i.promise=Sg(e).catch(()=>!1).then(e=>(i.resolved=e,e)),xg.set(e,i),i.promise}(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,()=>Rs(this._serverCfg)),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e,t){Jg.add(t);const i=Qg(e);return"undefined"==typeof window&&t(i),()=>{Jg.delete(t)}}(qf,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},Vf)),!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._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._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._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),this._modeTransition.dispose(),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(),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);for(const e of this._glowFadeTimers.values())clearTimeout(e);for(const e of this._glowEnterRafs.values())cancelAnimationFrame(e);clearTimeout(this._glowFeatherResumeTimer),this._glowFeatherResumeTimer=0,this._glowFeatherSuspendUntil=0,this._glowFadeTimers.clear(),this._glowEnterRafs.clear(),this._glowEnteringSources.clear(),this._glowRenderedSources.clear(),this._glowLastAppearance.clear(),this._glowSourceSeq=0,this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),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)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=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._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=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"]')),s=t.some(e=>e?.classList?.contains?.("editor-secondary")),n=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),a=/^[a-z]$/.test(o),r="z"===o||!a&&"KeyZ"===e.code,l="y"===o||!a&&"KeyY"===e.code,c=n&&(r&&e.shiftKey||l),h=n&&r&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoGeometry()):h?(e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==e.key&&"Backspace"!==e.key||!this._decorSel||i||s?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._furnPalette=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!s)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 s=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]/lv,e[1]/lv]),segments:this._draftSegmentCms.map((e,t)=>({...s[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 s=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;s?(this._activeDraftId=s.id,this._path=s.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,s.segments.map(e=>e.cm),s.id),this._resumeDraftBySpace[this._space]=s.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 getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of Xf.values())for(const t of e)clearTimeout(t.evict);Xf.clear(),ev=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const s of Xf.values())for(const n of s)e++,n.dlg&&(t++,i.push(n.dlg.kind));return{keys:Xf.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(sv)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(av)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=Qg(qf);try{const e=JSON.parse(localStorage.getItem(ov)||"null");this._kioskScale={icon:Es(e?.icon),font:Es(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(iv)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||Md(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Md(this._layout),this._virtualLights=mn(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=Qf(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),s=Xf.get(e);if(!s||!s.length)return;const n=s.find(e=>e.owner===this._warmGen);if(n)return this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=bd,clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,this._warmKey=e,n.frameFingerprint&&this._continuity.adoptCompleteFrame(n.frameFingerprint),void(!this._devices.length&&n.devices?.length&&(this._devices=[...n.devices]));const{slot:o,sure:a}=((e,t,i,s)=>{const n=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===s?4:e.live?0:t?3:2};let o=null,a=0,r=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=n(i);e<=0||(e>a?(o=i,a=e,r=1):e===a&&r++)}return!o||r>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=bd,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},Vf)),this._warmKey=e,a?(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},s.push(this._warmSlot),this._warmTrim(s)))}_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,s=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==s)return;var n;e.dlg=null,e.vp=(n=e.vp)&&"view"!==n.mode?{...n,mode:"view",zoom:n.snap?.space===n.space?n.snap.zoom:n.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:n,e.frameFingerprint="";const o=Xf.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},ev+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,this._adoptMode("view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const s=this._effectiveProjection(),n=s===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=n&&t.view?{...t.view}:null,this._viewModeSnap=n&&t.snap?{...t.snap}:null,!n&&t.logicalCenter){const e="iso"===s?sf([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var o;this._tool="opening"===(o=function(e){return"partition"===e?"draw":e}(o=t.tool))?"draw":"string"==typeof o&&dv.has(o)?o:"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}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=Qf(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 s=Xf.get(i)||[];for(s.push(this._warmSlot),Xf.set(i,s),this._warmTrim(s);Xf.size>8;){const e=Xf.keys().next().value;if(void 0===e||e===i)break;for(const t of Xf.get(e)||[])clearTimeout(t.evict);Xf.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(){this._warmRevivePending=!1;const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return;const t=e.dlg,i=e.freed;if(e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>ev)&&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()}}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=Md(this._serverCfg),this._layoutContentFingerprint=Md(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const s=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(yn).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>s.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(iv,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:fn(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,s]of i)if(s&&t.has(s))return{id:s,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=Th(e),s=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==s.kind)return"";const n=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===n)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return Cd([this._cfgRev,this._cfgContentFingerprint||Md(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Md(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 G;const e="connection"===this._continuity.recoveryReason;return W`
`}afterRender(){const e=this.host.root();this._blocked&&(this._openGroupId?this.closeGroup(!1):"palette"===this._currentModel?.kind&&this._currentModel.dismiss?.()),this._syncGlobalDismissListener();const t=e.querySelector(".editor-secondary"),i=this._renderedContext,s=t?.dataset.contextId||"";if(s&&i&&s!==i&&!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches&&(this._contentAnimation?.cancel(),this._contentAnimation=t?.querySelector(".editor-secondary-content")?.animate([{opacity:.35,transform:"translateY(-4px)"},{opacity:1,transform:"translateY(0)"}],{duration:100,easing:"ease-out"})),this._renderedContext=s,i&&s!==i&&this._focusOwned){const i=s?t?.querySelector('[tabindex="0"], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled])'):e.querySelector(".editbar-tools");i?.focus?.(),this._focusOwned=!!s}if("palette"===this._currentModel?.kind&&this._currentModel.launcherId){const t=[...e.querySelectorAll("[data-editor-palette]")].find(e=>e.dataset.editorPalette===this._currentModel?.launcherId);t&&null!==t.offsetParent||this._currentModel.dismiss?.()}if(!this._openGroupId)return;const n=[...e.querySelectorAll("[data-editor-group]")].find(e=>e.dataset.editorGroup===this._openGroupId);n&&null!==n.offsetParent||this.closeGroup(!0)}handleOutsideDismiss(e){if(K_(e,e=>e.hasAttribute("data-editor-navigation")))return"pointerdown"===e.type?this.closeForNavigation():"click"===e.type&&this._dismissPointerTarget&&this._clearDismissClick(),!1;if("click"===e.type&&this._dismissPointerTarget){return!!(e.composedPath?.()||[e.target]).includes(this._dismissPointerTarget)&&(this._clearDismissClick(),e.preventDefault(),e.stopImmediatePropagation(),!0)}const t=this._openGroupId||this._blocked||"palette"!==this._currentModel?.kind?null:this._currentModel;if("pointerdown"===e.type&&(this._openGroupId||t)){const i=K_(e,e=>e.classList.contains("editor-secondary")),s=K_(e,e=>!!e.dataset.editorGroup),n=K_(e,e=>!!e.dataset.editorPalette),o=K_(e,e=>e.classList.contains("stage"));if(!(i||s||n||"stay-open-on-canvas"===t?.dismissPolicy&&o))return e.preventDefault(),e.stopImmediatePropagation(),this._dismissPointerTarget=e.target,clearTimeout(this._dismissTimer),this._dismissTimer=window.setTimeout(()=>{this._clearDismissClick()},800),this._openGroupId?this.closeGroup(!1):t?.dismiss?.(),this._syncGlobalDismissListener(),!0}return!1}_activateGroupItem(e,t,i){if(e.id!==this._openGroupId)return;const s=e.items.find(e=>e.id===t);if(!s||s.disabled)return;const n="toggle"===s.role&&"stay-open"===s.closePolicy,o=0===i.detail;n||(this._openGroupId=null,this.host.requestUpdate(),this._syncGlobalDismissListener()),s.invoke(),!n&&o&&this.host.updateComplete().then(()=>{const t=[...this.host.root().querySelectorAll("[data-editor-group]")].find(t=>t.dataset.editorGroup===e.id&&null!==t.offsetParent);t?.focus()})}_groupKeydown(e){if(!["ArrowLeft","ArrowRight","Home","End"].includes(e.key))return;const t=[...e.currentTarget.querySelectorAll(".editor-group-item:not([disabled])")];if(!t.length)return;e.preventDefault();const i=t.indexOf(this.host.root().activeElement),s="Home"===e.key?0:"End"===e.key?t.length-1:"ArrowLeft"===e.key?i<=0?t.length-1:i-1:(i+1)%t.length;t[s]?.focus()}_clearDismissClick(){this._dismissPointerTarget=null,clearTimeout(this._dismissTimer),this._dismissTimer=void 0,this._syncGlobalDismissListener()}_syncGlobalDismissListener(){const e=!this._blocked&&"palette"===this._currentModel?.kind,t=!!this._openGroupId||e||!!this._dismissPointerTarget;t!==this._globalDismissListening&&(this._globalDismissListening=t,t?(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 J_=e=>e.stopPropagation(),X_=4.166666666666667e-9;function Z_(e){return Number.isFinite(e)?Math.round(e/Fh)*Fh:e}const Q_=(e,t,i,s)=>Math.hypot(i-e,s-t),em=(e,t,i,s,n,o,a,r)=>Math.hypot(Math.max(Math.abs(n-e),Math.abs(n+a-(e+i))),Math.max(Math.abs(o-t),Math.abs(o+r-(t+s)))),tm=(e,t,i,s,n,o,a)=>{const r=Math.max(Number(s)||0,0)/2,l=Math.PI/180,c=Math.cos(i*l)*r,h=Math.sin(i*l)*r,d=Math.cos(a*l)*r,p=Math.sin(a*l)*r,u=Math.max(Q_(e+c,t+h,n+d,o+p),Q_(e-c,t-h,n-d,o-p)),_=Math.max(Q_(e+c,t+h,n-d,o-p),Q_(e-c,t-h,n+d,o+p));return Math.min(u,_)},im=e=>{const t=Number(e?.cell_cm);return t>0?t:5};function sm(e,t){const i=JSON.parse(JSON.stringify(e||[])),s=JSON.parse(JSON.stringify(t||{}));let n=0,o=0,a=0,r=0,l="",c=0,h=0,d=0;const p=(e,t)=>{Number.isFinite(e)&&Number.isFinite(t)&&e!==t&&Math.abs(t-e)<=X_&&h++},u=(e,t)=>{p(e[0],t[0]),p(e[1],t[1])},_={};let m=5;for(const e of i){const t=im(e);null!=e?.id&&(_[String(e.id)]=t),t>m&&(m=t)}const g=(e,t,i,s=!1)=>{if(!(e>X_||s))return;n++,e>a&&(a=e);const o=e*Ah*t;o>r&&(r=o,l=i)};for(const e of i){const t=im(e),i=null!=e?.id?String(e.id):"";for(const s of e.rooms||[]){o++;let e=0;if(s.poly?.length)s.poly=s.poly.map(t=>{const i=[Z_(t[0]),Z_(t[1])];return e=Math.max(e,Q_(t[0],t[1],i[0],i[1])),u(t,i),i});else if(null!=s.x&&null!=s.y){const t=s.x,i=s.y,n=s.w||0,o=s.h||0,a=Z_(t+n),r=Z_(i+o),l=Z_(t),c=Z_(i),h=Math.max(Fh,a-l),d=Math.max(Fh,r-c);e=em(t,i,n,o,l,c,h,d),s.x=l,s.y=c,s.w=h,s.h=d,p(t,l),p(i,c),p(t+n,l+h),p(i+o,c+d)}g(e,t,i)}for(const s of e.room_drafts||[]){o++;let e=0;const n=s.points||[],a=n.map(t=>{const i=[Z_(t[0]),Z_(t[1])];return e=Math.max(e,Q_(t[0],t[1],i[0],i[1])),i}),r=a.length?[a[0]]:[],l=a.length?[[n[0],a[0]]]:[],c=[];for(let e=0;e+1=2)for(const[e,t]of l)u(e,t);g(e,t,i)}if(Array.isArray(e.room_drafts)){const t=e.room_drafts.length;e.room_drafts=e.room_drafts.filter(e=>e.points?.length>=2),d+=t-e.room_drafts.length,e.room_drafts.length||delete e.room_drafts}for(const s of e.partitions||[]){o++;const n=[s.a[0],s.a[1]],a=[s.b[0],s.b[1]],r=[Z_(n[0]),Z_(n[1])],l=[Z_(a[0]),Z_(a[1])];let c=Math.max(Q_(n[0],n[1],r[0],r[1]),Q_(a[0],a[1],l[0],l[1]));const h=Q_(r[0],r[1],l[0],l[1]),d=(e.openings||[]).filter(e=>"partition"===e.host?.kind&&e.host.id===s.id).every(e=>{const t=Number(e.length),i=Number(e.host.t),s=i*h;return Number.isFinite(t)&&t>0&&Number.isFinite(i)&&i>=0&&i<=1&&s-t/2>=-X_&&s+t/2<=h+X_});h>X_&&d?(s.a=r,s.b=l,u(n,r),u(a,l)):c=0,g(c,t,i)}for(const s of e.wall_columns||[]){o++;const e=[s.center[0],s.center[1]],n=[Z_(e[0]),Z_(e[1])],a=Q_(e[0],e[1],n[0],n[1]);s.center=n,u(e,n),g(a,t,i)}for(const s of e.decor||[]){o++;let e=0;if("line"===s.kind){const t=[s.x1,s.y1],i=[s.x2,s.y2],n=[Z_(t[0]),Z_(t[1])],o=[Z_(i[0]),Z_(i[1])];e=Math.max(Q_(t[0],t[1],n[0],n[1]),Q_(i[0],i[1],o[0],o[1])),s.x1=n[0],s.y1=n[1],s.x2=o[0],s.y2=o[1],u(t,n),u(i,o)}else{const t=s.x,i=s.y,n=s.w,o=s.h,a=Z_(t),r=Z_(i);if(null!=s.w&&null!=s.h){const l=Z_(t+n),c=Z_(i+o),h=Math.max(Fh,l-a),d=Math.max(Fh,c-r);e=em(t,i,n,o,a,r,h,d),s.w=h,s.h=d,p(t+n,a+h),p(i+o,r+d)}else e=Q_(t,i,a,r);s.x=a,s.y=r,p(t,a),p(i,r)}g(e,t,i)}for(const s of e.openings||[]){if(o++,"partition"===s.host?.kind){const n=(e.partitions||[]).find(e=>e.id===s.host.id),o=Number(s.host.t);if(!n||!Number.isFinite(o)||o<0||o>1)continue;const a=n.b[0]-n.a[0],r=n.b[1]-n.a[1];if(Math.hypot(a,r)<=X_)continue;const l=n.a[0]+a*o,h=n.a[1]+r*o;let d=180*Math.atan2(r,a)/Math.PI;d>=90?d-=180:d<-90&&(d+=180);const p=Number(s.angle),u=!(Number.isFinite(p)&&p===d),_=tm(s.x,s.y,Number.isFinite(p)?p:d,Number(s.length)||0,l,h,d);s.x=l,s.y=h,s.angle=d,u&&c++,g(_,t,i,u);continue}const n=ui([s.x,s.y],e.rooms||[],.025,{step:Fh,length:Number(s.length)||0});if(!n)continue;const a=Number(s.angle),r=!(Number.isFinite(a)&&a===n.angle),l=tm(s.x,s.y,Number.isFinite(a)?a:n.angle,Number(s.length)||0,n.x,n.y,n.angle);s.x=n.x,s.y=n.y,s.angle=n.angle,r&&c++,g(l,t,i,r)}}for(const[e,t]of Object.entries(s)){if(!t||"object"!=typeof t)continue;const i=t;if("number"!=typeof i.x||"number"!=typeof i.y)continue;o++;const n=Z_(i.x),a=Z_(i.y),r=Q_(i.x,i.y,n,a);s[e]={...i,x:n,y:a},p(i.x,n),p(i.y,a);const l="string"==typeof i.s?i.s:"";g(r,_[l]??m,l)}return{spaces:i,layout:s,report:{moved:n,coordsCanonicalized:h,total:o,maxShift:a,maxShiftCm:r,maxSpace:l,rotated:c,removedDrafts:d},changed:n>0||h>0}}function nm(e,t){const i=t.base+e*t.span;return Math.min(1,Math.max(0,i))}const om=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),am=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1]);function rm(e,t,i){const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;if(o<=0)return am(e,t);let a=((e[0]-t[0])*s+(e[1]-t[1])*n)/o;return a=Math.max(0,Math.min(1,a)),Math.hypot(e[0]-(t[0]+s*a),e[1]-(t[1]+n*a))}function lm(e,t,i,s,n){for(const s of t)if(s&&!i.has(s.id)&&om(s.a)&&om(s.b)&&(am(e,s.a)<=n||am(e,s.b)<=n))return!0;for(const t of s?.roomPolygons||[])for(let i=0;i0&&r>0))return null;if(Math.abs(n[0]/a*(o[1]/r)-n[1]/a*(o[0]/r))>i)return null;for(const[i,n]of[[e.a,t.a],[e.a,t.b],[e.b,t.a],[e.b,t.b]])if(am(i,n)<=s)return[(i[0]+n[0])/2,(i[1]+n[1])/2];return null}function hm(e,t){const i=.05*(Number(t.pitch)>0?Number(t.pitch):1),s=new Map;let n=(e||[]).filter(e=>!!e&&"string"==typeof e.id&&om(e.a)&&om(e.b));if(n.length<2)return{partitions:[...e||[]],merged:0,openingMoves:[]};const o=t.seedIds&&t.seedIds.length?new Set(t.seedIds):null;let a=0;for(let e=0;eam(t,e.at)>i);let h=c[0]??r.a,d=c[c.length-1]??l.b;if(c.length<2&&(h=r.a,d=l.b),d[0]0?(d[0]-h[0])/u:0,m=u>0?(d[1]-h[1])/u:0,g=e=>u>0?((e[0]-h[0])*_+(e[1]-h[1])*m)/u:0;for(const e of[r,l]){const t=g(e.a),i=g(e.b)-t;for(const[n,o]of s)o.toId===e.id&&s.set(n,{fromId:o.fromId,toId:p.id,base:t+o.base*i,span:o.span*i});s.set(e.id,{fromId:e.id,toId:p.id,base:t,span:i})}o&&o.add(p.id),n=n.filter((t,i)=>i!==e.i&&i!==e.j),n.splice(e.i,0,p),a++}return{partitions:n,merged:a,openingMoves:[...s.values()]}}function dm(e,t,i,s){if(!e?.length||!i.length)return 0;const n=new Map(i.map(e=>[e.fromId,e]));let o=0;for(const i of e){const e=i?.host;if(!e||"partition"!==e.kind)continue;const a=n.get(e.id);if(!a)continue;i.host={...e,id:a.toId,t:nm(e.t,a)},o++;const r=Tp(i,t,s.coordScale,s.cellCm,s.gridPitch).resolved;r&&Object.assign(i,Np(i,r,s.coordScale))}return o}function pm(e,t){const i=t?.excludeDraftId||null;return{roomPolygons:(e?.rooms||[]).map(e=>hi(e)).filter(e=>!!e),columns:e?.wall_columns||[],draftEnds:(e?.room_drafts||[]).flatMap(e=>{if(i&&e?.id===i)return[];const t=e?.points||[];return t.length?[t[0],t[t.length-1]]:[]})}}const um=e=>!!e&&e.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]),_m=(e,t)=>e[0]-t[0]||e[1]-t[1],mm=(e,t,i,s)=>[e[0]+t*s,e[1]+i*s],gm=(e,t,i,s)=>(e[0]-t[0])*i+(e[1]-t[1])*s,fm=(e,t,i,s,n)=>{const o=t[0]-e[0],a=t[1]-e[1],r=Math.hypot(o,a);if(!(r>n))return null;const l=o/r,c=a/r,h=t=>Math.abs((t[0]-e[0])*c-(t[1]-e[1])*l);if(h(i)>n||h(s)>n)return null;const d=gm(i,e,l,c),p=gm(s,e,l,c),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));return _-u>n?{lo:u,hi:_}:null},vm=(e,t,i)=>Math.min(e.hi,t.hi)-Math.max(e.lo,t.lo)>i,ym=e=>{const t=new Set(["id","a","b","cm"]);return Object.keys(e||{}).every(e=>t.has(e))},bm=(e,t,i,s)=>{const n=e.b[0]-e.a[0],o=e.b[1]-e.a[1],a=Math.hypot(n,o);if(!(a>s))return!0;const r=n/a,l=o/a,c=wl(e.cm,i.cellCm,i.gridPitch)/2;return t.some(t=>{const n=t?.center;if(!um(n))return!0;const o=gm(n,e.a,r,l),h=Math.abs((n[0]-e.a[0])*l-(n[1]-e.a[1])*r),d=wl(Number(t.cm),i.cellCm,i.gridPitch)*("square"===t.shape?Math.SQRT1_2:.5);return!(Number.isFinite(d)&&d>0)||o>=-d-s&&o<=a+d+s&&h<=c+d+s})},wm=(e,t,i)=>{if(e.host){const s=Tp(e,t,i.coordScale,i.cellCm,i.gridPitch).resolved;return s?{center:s.center,angle:s.angle,length:s.length}:null}const s=Number(e.x)*i.coordScale,n=Number(e.y)*i.coordScale,o=Number(e.angle),a=Number(e.length)*i.coordScale;return[s,n,o,a].every(Number.isFinite)&&a>0?{center:[s,n],angle:o,length:a}:null},km=(e,t,i)=>{const s=e.angle*Math.PI/180,n=Math.cos(s),o=Math.sin(s),a=t.angle*Math.PI/180,r=Math.cos(a),l=Math.sin(a);if(Math.abs(n*l-o*r)>1e-6)return!1;const c=t.center[0]-e.center[0],h=t.center[1]-e.center[1];if(Math.abs(c*o-h*n)>i)return!1;const d=c*n+h*o;return Math.max(-e.length/2,d-t.length/2)({...e,a:[e.a[0]*t,e.a[1]*t],b:[e.b[0]*t,e.b[1]*t]}),$m=(e,t,i,s)=>{const n=`~r-${(e=>{let t=2166136261;for(let i=0;i>>0;return t.toString(36)})(`${e}|${t[0].toFixed(9)},${t[1].toFixed(9)}|${i[0].toFixed(9)},${i[1].toFixed(9)}`)}`,o=`${e.slice(0,Math.max(1,64-n.length))}${n}`;let a=o;for(let e=2;s.has(a);e++){const t=`-${e}`;a=`${o.slice(0,64-t.length)}${t}`}return s.add(a),a},Sm=(e,t)=>{const i=[];for(const s of e){const e=i[i.length-1];!(e&&Math.abs(e.hi-s.lo)<=t&&e.safe===s.safe)||s.safe&&e.signature!==s.signature?i.push({lo:s.lo,hi:s.hi,a:s.a,b:s.b,safe:s.safe,signature:s.signature,roomIds:s.roomIds,finalCm:s.finalCm}):(e.hi=s.hi,e.b=s.b)}return i};function Mm(e,t,i,s,n){const o=i||[],a=Array.isArray(e?.partitions)?e.partitions:[],r=Array.isArray(e?.openings)?e.openings:[],l=Array.isArray(e?.room_drafts)?e.room_drafts:[],c={walls:o,partitions:a,openings:r,roomDrafts:l,partitionsReconciled:0,openingsRehosted:0,removedDrafts:0};if(!a.length&&!l.length)return c;const h=Math.max(2e-4*n.gridPitch,1e-9);let d=o,p=a,u=r;const _=gc(t.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),m=((e,t,i,s)=>{const n=new Map(e.map(e=>[e.id,e])),o=new Set;for(const e of t){if(!n.get(e.id)||!Array.isArray(e.points)||e.points.length<2||e.segments.length!==e.points.length-1)continue;let t=!0;for(let n=0;n+10)){t=!1;break}const l=Math.hypot(a[0]-o[0],a[1]-o[1]);if(!(l>s)){t=!1;break}const c=i.flatMap(e=>{const t=e.cm>0?e.cm:ul;if(e.open||"outer"!==e.kind&&"shared"!==e.kind||t+se.lo-t.lo||e.hi-t.hi);let h=0;for(const e of c){if(e.lo>h+s)break;if(h=Math.max(h,e.hi),h>=l-s)break}h!m.has(e.id));let f=0,v=0;const y=new Map(t.partitions.map(e=>[e.id,e]));for(const e of[...a].sort((e,t)=>e.id.localeCompare(t.id))){if(!p.some(t=>t.id===e.id))continue;if(!ym(e))continue;const i=y.get(e.id);if(!(i&&um(i.a)&&um(i.b)&&Number.isFinite(i.cm)&&i.cm>0))continue;const[o,a]=_m(i.a,i.b)<=0?[[i.a[0],i.a[1]],[i.b[0],i.b[1]]]:[[i.b[0],i.b[1]],[i.a[0],i.a[1]]],r=a[0]-o[0],l=a[1]-o[1],c=Math.hypot(r,l);if(!(c>h))continue;const _=r/c,g=l/c,b=gc(t.rooms,d,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),w=b.flatMap(e=>{if(e.open||"outer"!==e.kind&&"shared"!==e.kind)return[];const t=fm(o,a,e.a,e.b,h);return t?[{interval:e,range:t}]:[]});if(!w.length)continue;const k=u.filter(e=>"partition"===e.host?.kind&&e.host.id===i.id).map(e=>({opening:e,resolved:Tp(e,t.partitions,n.coordScale,n.cellCm,n.gridPitch).resolved}));if(k.some(e=>!e.resolved))continue;const x=[0,c];for(const{range:e}of w)x.push(e.lo,e.hi);const $=x.slice(),S=new Map;for(const{opening:e,resolved:t}of k){const i=gm(t.center,o,_,g),s={lo:i-t.length/2,hi:i+t.length/2};S.set(e.id,s),$.push(Math.max(0,s.lo),Math.min(c,s.hi))}const M=[...new Set($.filter(e=>Number.isFinite(e)&&e>=-h&&e<=c+h).map(e=>Math.max(0,Math.min(c,e)).toFixed(9)))].map(Number).sort((e,t)=>e-t),C=p.map(e=>xm(e,n.coordScale)).filter(e=>e.id!==i.id),D=t.room_drafts.filter(e=>!m.has(e.id)),P=[];for(let e=0;e+1h))continue;const r=mm(o,_,g,s),l=mm(o,_,g,a),c=w.filter(({range:e})=>e.lo<=s+h&&e.hi>=a-h).map(({interval:e})=>e),d=new Map;for(const e of c)d.set(e.roomId,e);const p=[...d.values()],u=new Set(p.map(e=>e.kind)),m=new Set(p.map(e=>e.cm>0?e.cm:ul)),f=p[0]?.kind,v=[...d.keys()].sort(),y=1===u.size&&1===m.size&&("outer"===f&&1===v.length||"shared"===f&&2===v.length),b={id:i.id,a:r,b:l,cm:i.cm},k=C.some(e=>!!fm(r,l,e.a,e.b,h)),x=D.some(e=>e.points.some((t,i)=>i+10?p[0].cm:ul:0,T=y?Math.max(S,i.cm):i.cm;P.push({lo:s,hi:a,a:r,b:l,safe:$,signature:$?`${f}|${v.join(",")}|${T}`:"",roomIds:v,roomCm:S,finalCm:T})}if(!P.some(e=>e.safe))continue;const T=new Map;for(const{opening:e,resolved:t}of k){const s=S.get(e.id),o=x.some(e=>e>s.lo+h&&evm(e,s,h)),r=new Set(a.filter(e=>e.safe).map(e=>e.signature));if(!o&&a.length>0&&a.every(e=>e.safe)&&1===r.size&&Ep(t,b,h)){T.set(e.id,{resolved:t,signature:[...r][0]});continue}const l=Dp(i,n.cellCm,n.gridPitch),c={lo:s.lo-l,hi:s.hi+l};for(const e of P)vm(e,c,h)&&(e.safe=!1,e.signature="")}let I=!0;for(;I;){I=!1;for(const[e,t]of[...T.entries()]){const s=S.get(e),o=P.filter(e=>vm(e,s,h));if(o.length>0&&o.every(e=>e.safe)&&o.every(e=>e.signature===t.signature))continue;T.delete(e);const a=Dp(i,n.cellCm,n.gridPitch),r={lo:s.lo-a,hi:s.hi+a};for(const e of P)vm(e,r,h)&&(e.safe=!1,e.signature="");I=!0}}const R=Sm(P,h),A=R.filter(e=>e.safe),z=R.filter(e=>!e.safe);if(!A.length)continue;if(p.length-1+z.length>2e3)continue;let F=d;for(const e of A){const[t,s]=_m(i.a,i.b)<=0?[e.a,e.b]:[e.b,e.a];F=Hl(F,t,s,e.finalCm,n.pitch,n.coordScale)}if(F=vc(t.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale),F.length>500)continue;const E=new Set(p.map(e=>e.id));E.delete(i.id);const N=z.map((e,t)=>{const s=[e.a[0]/n.coordScale,e.a[1]/n.coordScale],o=[e.b[0]/n.coordScale,e.b[1]/n.coordScale],a=0===t?i.id:$m(i.id,s,o,E);return E.add(a),{id:a,a:s,b:o,cm:i.cm}}),O=p.findIndex(e=>e.id===i.id),L=p.slice();L.splice(O,1,...N);const H=L.map(e=>xm(e,n.coordScale)),B=new Map;let q=!0;for(const{opening:e,resolved:t}of k){const i=T.get(e.id);if(i){const t=Np(e,i.resolved,n.coordScale),{host:s,...o}=t;B.set(e.id,o);continue}const s=S.get(e.id),o=N[z.findIndex(e=>e.lo<=s.lo+h&&e.hi>=s.hi-h)],a=o&&H.find(e=>e.id===o.id);if(!o||!a){q=!1;break}const r=a.b[0]-a.a[0],l=a.b[1]-a.a[1],c=Math.hypot(r,l),d=gm(t.center,a.a,r/c,l/c),p={...Np(e,t,n.coordScale),host:{kind:"partition",id:o.id,t:d/c}};if(!Ip(p,H,n.coordScale,n.cellCm,n.gridPitch).resolved){q=!1;break}B.set(e.id,p)}if(!q)continue;const W=u.map(e=>B.get(e.id)||e),j=[...T.keys()].map(e=>B.get(e)),U=j.map(e=>wm(e,H,n));if(U.some(e=>!e))continue;let G=!1;for(let e=0;ee.id));for(const e of W){if(V.has(e.id))continue;const t=wm(e,H,n);t&&U.some(e=>km(e,t,h))&&(G=!0)}if(G)continue;const K=Bc(t.rooms,F,s,n.pitch,n.cellCm,n.gridPitch,n.coordScale);[...T.entries()].every(([e,t])=>{const i=B.get(e),s=jc(K,{x:i.x*n.coordScale,y:i.y*n.coordScale,angle:i.angle,length:i.length*n.coordScale},!0),o=[s.negative,s.positive].filter(e=>!!e?.full),a=t.signature.split("|")[1].split(","),r=new Set(o.map(e=>e.roomId));return o.length===a.length&&r.size===a.length&&a.every(e=>r.has(e))})&&(d=F,p=L,u=W,f+=A.length,v+=T.size)}return{walls:d,partitions:p,openings:u,roomDrafts:g,partitionsReconciled:f,openingsRehosted:v,removedDrafts:m.size}}const Cm=e=>JSON.parse(JSON.stringify(e)),Dm=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),Pm=(e,t)=>t.length>0&&t.length<=35&&("space"===e?/^[a-z0-9_-]+$/.test(t):/^[A-Za-z0-9_-]+$/.test(t));function Tm(e,t){let i=String(t??"");const s=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`^${s}_(.+)_([0-9a-f]{8})$`);let o=0;for(;o<16;o++){const e=n.exec(i);if(!e)return{root:i,layers:o,bounded:!1};i=e[1]}return{root:i,layers:o,bounded:n.test(i)}}const Im=(e,t,i)=>{const s=e.get(t)||[];s.push(i),e.set(t,s)};const Rm=1e-9;class Am extends Error{constructor(e,t=""){super(t?`${e}: ${t}`:e),this.reason=e,this.name="WallSegmentModelError"}}const zm=e=>JSON.parse(JSON.stringify(e)),Fm=e=>Array.isArray(e)&&e.length>=2&&Number.isFinite(Number(e[0]))&&Number.isFinite(Number(e[1])),Em=e=>[Number(e[0]),Number(e[1])],Nm=e=>`${Number(e[0]).toFixed(12)},${Number(e[1]).toFixed(12)}`,Om=(e,t)=>{const i=Nm(e),s=Nm(t);return iNm(e)<=Nm(t)?[Em(e),Em(t)]:[Em(t),Em(e)],Hm=(e,t)=>Math.hypot(t[0]-e[0],t[1]-e[1]),Bm=e=>{const t=[[Number(e.points[0][0]),Number(e.points[0][1])]],i=[];for(let s=1;s{const i=new Set,s=e=>{const t=Number(e);if(!Number.isFinite(t))return;const s=t/Fh;Math.abs(s-Math.round(s))>=Jr&&i.add(t.toFixed(12))};for(const t of Array.isArray(e?.rooms)?e.rooms:[])for(const e of hi(t)||[])s(e[0]),s(e[1]);for(const t of[e?.wall_segments,e?.walls])for(const e of Array.isArray(t)?t:[])s(e?.a?.[0]),s(e?.a?.[1]),s(e?.b?.[0]),s(e?.b?.[1]);for(const e of t)s(e?.[0]),s(e?.[1]);return i.size},Wm=(e,t,i)=>{const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;return o>1e-18?((e[0]-t[0])*s+(e[1]-t[1])*n)/o:0},jm=(e,t,i)=>{const s=Math.max(0,Math.min(1,Wm(e,t,i)));return Math.hypot(e[0]-(t[0]+(i[0]-t[0])*s),e[1]-(t[1]+(i[1]-t[1])*s))},Um=(e,t,i,s,n=Rm)=>{const o=t[0]-e[0],a=t[1]-e[1],r=Math.hypot(o,a);if(r<=n)return 0;const l=t=>Math.abs((t[0]-e[0])*a-(t[1]-e[1])*o)/r;if(l(i)>n||l(s)>n)return 0;const c=Wm(i,e,t),h=Wm(s,e,t);return Math.max(0,Math.min(1,Math.max(c,h))-Math.max(0,Math.min(c,h)))*r},Gm=e=>{const t=(new TextEncoder).encode(e),i=8*t.length,s=64*Math.ceil((t.length+9)/64),n=new Uint8Array(s);n.set(t),n[t.length]=128;const o=new DataView(n.buffer);o.setUint32(s-8,Math.floor(i/4294967296),!1),o.setUint32(s-4,i>>>0,!1);const a=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),c=(e,t)=>e>>>t|e<<32-t;for(let e=0;e>>3,n=c(i,17)^c(i,19)^i>>>10;l[e]=l[e-16]+s+l[e-7]+n>>>0}let[t,i,s,n,h,d,p,u]=r;for(let e=0;e<64;e++){const o=u+(c(h,6)^c(h,11)^c(h,25))+(h&d^~h&p)+a[e]+l[e]>>>0,r=t&i^t&s^i&s;u=p,p=d,d=h,h=n+o>>>0,n=s,s=i,i=t,t=o+((c(t,2)^c(t,13)^c(t,22))+r>>>0)>>>0}r[0]=r[0]+t>>>0,r[1]=r[1]+i>>>0,r[2]=r[2]+s>>>0,r[3]=r[3]+n>>>0,r[4]=r[4]+h>>>0,r[5]=r[5]+d>>>0,r[6]=r[6]+p>>>0,r[7]=r[7]+u>>>0}const h=new Uint8Array(32),d=new DataView(h.buffer);for(let e=0;e{const t="abcdefghijklmnopqrstuvwxyz234567";let i=0,s=0,n="";for(const o of e)for(s=s<<8|o,i+=8;i>=5;)n+=t[s>>>i-5&31],i-=5;return i&&(n+=t[s<<5-i&31]),n},Km=(e,t,i,s,n="")=>{const[o,a]=Lm(t,i),r=`${e}|${Nm(o)}|${Nm(a)}|${[...s].sort().join(",")}${n}`;return`wall-${Vm(Gm(r)).slice(0,20)}`};let Ym=0;const Jm=e=>{for(let t=0;t<1e3;t++){const t=globalThis.crypto?.randomUUID?.(),i=`wall-${t||Vm(Gm(`${Date.now()}|${Ym++}|${Math.random()}`)).slice(0,26)}`;if(!e.has(i))return i}throw new Am("duplicate-id","id factory exhausted")},Xm=e=>{const t=new Map;for(const i of Array.isArray(e?.wall_segments)?e.wall_segments:[])if(i&&"string"==typeof i.id&&Fm(i.a)&&Fm(i.b)){if(t.has(i.id))throw new Am("duplicate-id",i.id);t.set(i.id,i)}return t},Zm=(e,t,i)=>{const s=(i,s)=>{const n=e[0]-i[0],o=e[1]-i[1];return Math.abs(t[0]-s[0]-n)<=Rm&&Math.abs(t[1]-s[1]-o)<=Rm?[n,o]:null};return s(i.a,i.b)||s(i.b,i.a)},Qm=(e,t)=>{const i=Array.isArray(e.rooms)?e.rooms:[],s=vd(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]),n=s.length?s:yd(i,null,1,.04*Fh,!0),o=[];for(const e of i){const i=hi(e),s=Array.isArray(e?.wall_ids)?e.wall_ids:[];if(i&&s.length===i.length)for(let e=0;e"partition"!==e.host?.kind&&[Number(e.x),Number(e.y)].every(Number.isFinite)&&Number.isFinite(Number(e.angle))&&Number(e.length)>0&&n.some(t=>jm([Number(e.x),Number(e.y)],[t[0],t[1]],[t[2],t[3]])<=.04*Fh)):[],l=r.map(e=>{const t=Number(e.angle)*Math.PI/180,i=[Math.cos(t),Math.sin(t)],s=Number(e.length)/2,n=[Number(e.x),Number(e.y)];return{a:[n[0]-i[0]*s,n[1]-i[1]*s],b:[n[0]+i[0]*s,n[1]+i[1]*s]}}),c=(e,t)=>r.some(i=>{const s=[Number(i.x),Number(i.y)];if(!El(e,t,Number(i.angle)))return!1;if(jm(s,e,t)>.02*Fh)return!1;const n=Hm(e,t),o=Wm(s,e,t)*n,a=Number(i.length)/2;return o+a>=-Rm&&o-a<=n+Rm}),h=new Map,d=[];for(const s of i){const r=String(s?.id||""),p=hi(s);if(!r||!p||p.length<3)throw new Am("invalid-room",r);const u=ac(i,r,a,Fh,1,[...e.walls||[],...l]);if(!u||u.poly.length<3)throw new Am("invalid-room",r);const _=Array.isArray(s.wall_ids)?s.wall_ids:[],m=_.length===p.length;let g=null,f=m;if(f)for(let e=0;eRm||Math.abs(s[1]-g[1])>Rm)){f=!1;break}g=s}const v=[];for(let e=0;e2)throw new Am("third-owner",a);const d=m?_[u.parent[e]]:void 0;if("string"==typeof d&&d){const n=t.get(d);f||!n||Um(i,s,n.a,n.b)>Rm?l.preferredIds.add(d):l.positionalIds.add(d),l.preferredCarriers.set(d,{a:Em(p[u.parent[e]]),b:Em(p[(u.parent[e]+1)%p.length])})}const g=u.parent[e];l.parentKeys.add(Ml(p[g],p[(g+1)%p.length],Fh));const y=[(i[0]+s[0])/2,(i[1]+s[1])/2],b=e=>e.some(e=>jm(y,[e[0],e[1]],[e[2],e[3]])<=.04*Fh);l.zeroWall||=b(o)||b(n)&&!c(i,s),v.push(a)}const y={...s};delete y.wall_ids,d.push({...y,poly:u.poly.map(e=>[e[0],e[1]]),wall_ids:v})}return{atoms:[...h.values()].sort((e,t)=>e.key.localeCompare(t.key)),rooms:d}},eg=(e,t,i,s,n)=>{const o=new Map([...i.values()].map(e=>[Om(e.a,e.b),e])),a=(e=>{const t=new Map;for(const i of e)"wall"===i.host?.kind&&t.set(i.host.id,(t.get(i.host.id)||0)+1);return t})(e.openings||[]),r=new Map;for(const e of t){const t=n?.get(e.key);if(t){const s=i.get(t);if(!s)throw new Am("duplicate-id",t);r.set(e,s);continue}if(e.preferredIds.size>1)throw new Am("duplicate-id",[...e.preferredIds].sort().join(","));const s=[...e.preferredIds][0],l=s?i.get(s):void 0;if(l){r.set(e,l);continue}const c=s?e.preferredCarriers.get(s):void 0;if(s&&c){r.set(e,{id:s,a:c.a,b:c.b,cm:0});continue}const h=o.get(e.key);if(h){r.set(e,h);continue}const d=[...i.values()].filter(t=>Um(e.a,e.b,t.a,t.b)>Rm),p=[...new Map(d.map(e=>[e.id,e])).values()];if(p.sort((e,t)=>(a.get(t.id)||0)-(a.get(e.id)||0)||Hm(t.a,t.b)-Hm(e.a,e.b)||e.id.localeCompare(t.id)),p[0])r.set(e,p[0]);else if(1===e.positionalIds.size){const t=i.get([...e.positionalIds][0]);t&&r.set(e,t)}}const l=new Map;for(const[e,t]of r){const i=l.get(t.id)||[];i.push(e),l.set(t.id,i)}for(const[e,t]of l){const s=i.get(e)||r.get(t[0]);if(!s)throw new Am("duplicate-id",e);const n=[(s.a[0]+s.b[0])/2,(s.a[1]+s.b[1])/2];t.sort((e,t)=>{const i=jm(n,e.a,e.b)<=Rm?0:1,o=jm(n,t.a,t.b)<=Rm?0:1;if(i!==o)return i-o;return(jm(s.a,e.a,e.b)<=Rm?0:1)-(jm(s.a,t.a,t.b)<=Rm?0:1)||e.key.localeCompare(t.key)}),t[0].id=e}const c=(e=>{const t=new Set;for(const i of[e.rooms,e.openings,e.decor,e.room_drafts,e.partitions,e.wall_columns])for(const e of Array.isArray(i)?i:[])"string"==typeof e?.id&&e.id&&t.add(e.id);for(const i of Array.isArray(e.room_drafts)?e.room_drafts:[])for(const e of Array.isArray(i?.segments)?i.segments:[])"string"==typeof e?.id&&e.id&&t.add(e.id);return t})(e);for(const e of t)if(e.id){if(c.has(e.id))throw new Am("duplicate-id",e.id);c.add(e.id)}const h=t.filter(e=>!e.id);if(s){const t=h.map(t=>{const[i,s]=Lm(t.a,t.b),n=`${String(e.id||"")}|${Nm(i)}|${Nm(s)}|${[...t.owners].sort().join(",")}`,o=Vm(Gm(n));return{atom:t,seed:n,digest:o,base:`wall-${o.slice(0,20)}`}}).sort((e,t)=>e.digest.localeCompare(t.digest)||e.atom.key.localeCompare(t.atom.key)),i=new Map;for(const e of t){const t=i.get(e.digest);if(t&&t!==e.seed)throw new Am("duplicate-id",e.digest);i.set(e.digest,e.seed);let s=1,n=e.base;for(;c.has(n);)n=`${e.base}-${++s}`;e.atom.id=n,c.add(n)}}else for(const e of h)e.id=Jm(c),c.add(e.id)},tg=(e,t)=>{if("partition"===e.host?.kind)return null;const i=[Number(e.x),Number(e.y)];if(!i.every(Number.isFinite))return null;const s=t=>{if(!(Number(t.cm)>0))return!1;const s=Wm(i,t.a,t.b),n=Hm(t.a,t.b),o=Number(e.length)/2;return s>=-Rm&&s<=1+Rm&&jm(i,t.a,t.b)<=.02*Fh&&El(t.a,t.b,Number(e.angle))&&Number.isFinite(o)&&o>=0&&s*n-o>=-Rm&&s*n+o<=n+Rm},n="wall"===e.host?.kind?t.find(t=>t.id===e.host.id):null,o=n&&s(n)?[n]:t.filter(s);if(1!==o.length)return null;const a=o[0];return{kind:"wall",id:a.id,t:Math.max(0,Math.min(1,Wm(i,a.a,a.b)))}},ig=(e,t)=>{const i=[Number(e.x),Number(e.y)];if(!i.every(Number.isFinite))return null;const s=Number(e.length)/2,n=t.filter(t=>{if(!(Number(t.cm)>0))return!1;const n=Wm(i,t.a,t.b),o=Hm(t.a,t.b);return n>=-Rm&&n<=1+Rm&&jm(i,t.a,t.b)<=.02*Fh&&El(t.a,t.b,Number(e.angle))&&Number.isFinite(s)&&s>=0&&n*o-s>=-Rm&&n*o+s<=o+Rm}),o=(t=>{if(!t.length)return null;const s="wall"===e.host?.kind?t.find(t=>t.id===e.host.id):null;return s||[...t].sort((e,t)=>jm(i,e.a,e.b)-jm(i,t.a,t.b)||Number(t.cm)-Number(e.cm)||(e.idt.id?1:0))[0]})(n);return o?{kind:"wall",id:o.id,t:Math.max(0,Math.min(1,Wm(i,o.a,o.b)))}:null},sg=(e,t,i)=>{const s=Xm(e),{atoms:n,rooms:o}=Qm(e,s);eg(e,n,s,t,i);const a=n.map(t=>{const i=t.id?s.get(t.id):void 0,n=((e,t,i)=>{if(t.zeroWall)return 0;const s=Array.isArray(e.walls)?e.walls:[],n=Ml(t.a,t.b,Fh),o=s.filter(e=>Number(e.cm)>0&&(!(e.key!==n&&!t.parentKeys.has(e.key))||!(!Fm(e.a)||!Fm(e.b))&&Um(t.a,t.b,e.a,e.b)>=Hm(t.a,t.b)-Rm)).map(e=>Math.max(1,Math.min(100,Number(e.cm))));if(new Set(o.map(e=>e.toFixed(9))).size>1)throw new Am("thickness-conflict",t.key);if(o.length)return o[0];const a=zl(s,t.a,t.b,Fh,1);return a>0?a:Number(i?.cm)>0?Number(i.cm):0})(e,t,i);return{...i||{},id:t.id,a:[...t.a],b:[...t.b],cm:n}}),r=new Map(n.map(e=>[e.key,e.id]));for(const e of o)e.wall_ids=e.wall_ids.map(e=>r.get(e));e.rooms=o,e.wall_segments=a,e.walls=a.filter(e=>e.cm>0).map(e=>({key:Ml(e.a,e.b,Fh),cm:e.cm,a:[...e.a],b:[...e.b]})),e.walls.length||delete e.walls,delete e.open_spans;for(const t of e.rooms)delete t.open_to;return((e,t)=>{const i=new Set;for(const t of[e.rooms,e.openings,e.decor,e.room_drafts,e.partitions,e.wall_columns,e.wall_segments])for(const e of Array.isArray(t)?t:[])"string"==typeof e?.id&&e.id&&i.add(e.id);for(const s of Array.isArray(e.room_drafts)?e.room_drafts:[]){const n=Array.isArray(s.segments)?s.segments:[];for(let o=0;o{const s=Array.isArray(e.openings)?e.openings:[];for(const e of s){if("partition"===e.host?.kind)continue;if(!e.host&&!i){const i=tg(e,t);i&&(e.host=i);continue}const s=tg(e,t)??(i?ig(e,t):null);if(s)e.host=s;else{if(!i)throw new Am("opening-host",e.id);delete e.host}}})(e,a,t),a.reduce((e,t)=>e+(s.has(t.id)?0:1),0)};function ng(e,t={}){const i=JSON.stringify(e),s=zm(e);pl(s);let n=0;const o=Number(s?.model_version||0)<9;for(const e of Array.isArray(s?.spaces)?s.spaces:[]){const i=t.lineageSpaceId===String(e?.id||"")?t.lineageHints:void 0;n+=sg(e,o,i)}return s.model_version=9,pl(s),{config:s,changed:i!==JSON.stringify(s),migratedSegments:n}}const og=e=>!!e&&"object"==typeof e&&!Array.isArray(e);function ag(e,t){const i=(e,t)=>{if(Array.isArray(e)&&Array.isArray(t)){const s=new Map;for(const t of e)og(t)&&"string"==typeof t.id&&t.id&&s.set(t.id,t);const n=t.map((t,n)=>{if(og(t)&&"string"==typeof t.id&&t.id){const e=s.get(t.id);return e?i(e,t):zm(t)}return n[t,t in e?i(e[t],s):zm(s)]);for(const t of Object.keys(e))delete e[t];for(const[t,i]of s)e[t]=i;return e}return zm(t)};return i(e,t)}const rg=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),lg=(e,t,i)=>Math.min(i,Math.max(t,e)),cg=e=>Th({spaces:[e]})[0],hg=(e,t,i)=>{const s=i>0?i:1,n=e=>`${(e[0]/s).toFixed(12)},${(e[1]/s).toFixed(12)}`,o=n(e),a=n(t);return o0?a:1,l=Math.max(s*r*.02,1e-9),c=[];for(const t of e||[])for(const e of hi(t)||[])c.push([e[0],e[1]]);const h=[];for(const e of i||[])Array.isArray(e)&&e.length>=4&&e.slice(0,4).every(Number.isFinite)&&h.push([e[0],e[1]],[e[2],e[3]]);const d=(e,t)=>t.some(t=>Math.hypot(e[0]-t[0],e[1]-t[1])<=2*l),p=new Map;for(const a of e||[]){if(!a?.id)continue;const u=mc(e,a.id,t,i,s,n,o,r);if(u)for(let e=0;e0&&_>0&&a===m&&_!==a))continue;const g=u.poly[s],f=u.poly[(s+1)%u.poly.length],v=Math.hypot(f[0]-g[0],f[1]-g[1]);if(!(v>l&&v<.5*o-1e-9*o))continue;if(d(g,h)||d(f,h))continue;if(d(g,c)&&d(f,c))continue;const y=hg(g,f,r),b=p.get(y);b?b.targets.add(a):p.set(y,{a:[g[0],g[1]],b:[f[0],f[1]],targets:new Set([a])})}}}let u=t.slice();const _=(e,t)=>Math.hypot(e[0]-t[0],e[1]-t[1])<=2*l,m=(e,t)=>{if(!Array.isArray(e.a)||!Array.isArray(e.b)||e.a.length<2||e.b.length<2)return!1;const i=[Number(e.a[0])*r,Number(e.a[1])*r],s=[Number(e.b[0])*r,Number(e.b[1])*r];return!![...i,...s].every(Number.isFinite)&&(_(i,t.a)&&_(s,t.b)||_(i,t.b)&&_(s,t.a))};for(const e of[...p.values()].filter(e=>1===e.targets.size).sort((e,t)=>hg(e.a,e.b,r).localeCompare(hg(t.a,t.b,r)))){const t=[...e.targets][0],i=u.filter(t=>m(t,e));if(new Set(i.map(e=>e.cm)).size>1)continue;let n=!1;u=u.map(i=>m(i,e)?(n=!0,i.cm===t?i:{...i,cm:t}):i),n||(u=Hl(u,e.a,e.b,t,s,r))}return u}const pg=e=>{const t=[];for(const i of e||[]){const e=hi(i);if(e?.length)for(let i=0;i"string"==typeof e?.id?e.id:"").filter(Boolean),h=new Set(c),d=new Map;for(const e of c){const t=Tm("space",e);t.layers>0&&Pm("space",t.root)&&Im(d,t.root,e)}const p=new Map,u=new Map,_=new Map,m=new Map;for(const e of r){const t="string"==typeof e?.id?e.id:"";if(!t)continue;const i=new Map;for(const s of Array.isArray(e.rooms)?e.rooms:[]){const e="string"==typeof s?.id?s.id:"";if(!e)continue;const n=Tm("room",e);n.layers>0&&Pm("room",n.root)&&Im(i,n.root,e),u.has(e)||u.set(e,t),_.has(e)||_.set(e,String(s.name||""));const o="string"==typeof s.area?s.area:"";if(o){const i=m.get(o)||[];i.push({spaceId:t,roomId:e}),m.set(o,i)}}p.set(t,i)}const g=new Set(u.keys()),f=new Map,v=new Map;for(const e of l)"string"==typeof e?.id&&e.id&&(!0===e.removed?v.set(e.id,e):f.set(e.id,e));const y={spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,positionsUnresolved:0,nestedRefsUnresolved:0,deadSpaceIds:[],orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0,removedPositions:[],liveMissingPositions:[],unverifiedPositions:[]},b=new Set,w=e=>h.has(e)?null:(()=>{const t=Tm("space",e).root;if(!Pm("space",t))return null;const i=d.get(t)||[];return 1===i.length?i[0]:null})(),k=(e,t)=>{const i=Tm("room",e).root;if(!Pm("room",i))return null;const s=p.get(t)?.get(i)||[];return 1===s.length?s[0]:null},x=e=>{const t=i.effectiveAreaByMarker?.[e]||"",s=t&&m.get(t)||[];return 1===s.length?s[0]:null},$=(e,t,i)=>{if("string"==typeof e.room_id&&e.room_id&&!g.has(e.room_id)){const s=t?k(e.room_id,t):null;s?(e.room_id=s,y.roomRefsRemapped++):i?(e.room_id=i.roomId,y.roomRefsRemapped++):!0!==e.removed&&delete e.room_id}const s=e.vacuum?.segment_map;if(s&&"object"==typeof s&&!Array.isArray(s))for(const e of Object.keys(s)){const i=s[e];if("string"!=typeof i||!i||g.has(i))continue;const n=t?k(i,t):null;n?(s[e]=n,y.roomRefsRemapped++):y.nestedRefsUnresolved++}};for(const e of l){const t="string"==typeof e?.id?e.id:"";if(!t)continue;const i="string"==typeof e.space?e.space:"",s=i&&!h.has(i),o=n[t],a="string"==typeof o?.s?o.s:"",r=a&&!h.has(a),l=!0===e.removed;let c=i&&h.has(i)?i:null,d=null,p=!1;if(s){const s=w(i);s?(e.space=s,c=s,p=!0,y.spaceRefsRemapped++):l||"virtual"===e.binding?l||(delete e.space,c=null,y.markersDetached++):(d=x(t),d?(e.space=d.spaceId,c=d.spaceId,y.spaceRefsRemapped++):(delete e.space,c=null,y.markersDetached++))}l&&!p||$(e,c,d),r&&p&&a===i&&c&&(n[t]={...o,s:c},y.positionsRemapped++,b.add(t))}for(const e of Object.keys(n)){if(b.has(e))continue;const t=n[e],i="string"==typeof t?.s?t.s:"";if(!i||h.has(i))continue;const s=w(i);if(s){if(e.startsWith("rl_")){const i=e.slice(3),o=k(i,s);if(o){const i=`rl_${o}`;Dm(n,i)||(n[i]={...t,s:s}),delete n[e],y.roomRefsRemapped++,y.positionsRemapped++;continue}}n[e]={...t,s:s},y.positionsRemapped++}}const S=i.ownerRoster,M=!0===S?.authoritative,C=new Set(S?.deviceIds||[]),D=new Set(S?.entityIds||[]),P=new Set(Array.isArray(s.settings?.known_devices)?s.settings.known_devices.filter(e=>"string"==typeof e):[]),T=(e,t="")=>{const i=String(S?.names?.[e]||t||"").trim();return i&&i!==e?i:""},I=(e,t,i,s="",n)=>({id:e,spaceId:t,kind:i,name:T(e,s),...n?{reason:n}:{}}),R=e=>{"room_label"===e?y.orphanRoomLabelsRemoved++:"group"===e?y.orphanGroupPositionsRemoved++:y.orphanDevicePositionsRemoved++},A=new Set;for(const[e,t]of Object.entries(n)){const s="string"==typeof t?.s?t.s:"";if(!s||h.has(s))continue;let o,a;const r=f.get(e),l=v.get(e);if(r)o=I(e,s,"device",String(r.name||"")),a="live";else if(l)o=I(e,s,"device",String(l.name||"")),a="absent";else if(e.startsWith("rl_")){const t=e.slice(3);o=I(e,s,"room_label",_.get(t)||""),a=g.has(t)?"live":"absent"}else if(e.startsWith("lg_")){const t=e.slice(3);o=I(e,s,"group"),a=D.has(t)?"live":M?"absent":"unverified","unverified"===a&&(o.reason="registry_unavailable")}else C.has(e)?(o=I(e,s,"device"),a="live"):P.has(e)?(o=I(e,s,"device"),a=M?"absent":"unverified","unverified"===a&&(o.reason="registry_unavailable")):(o=I(e,s,"unknown","","unknown_owner"),a="unverified");if("absent"!==a){if("live"===a){if(y.liveMissingPositions.push(o),!0===i.removeLiveMissingPositions){delete n[e],y.liveMissingPositionsRemoved++,R(o.kind),y.removedPositions.push(o);continue}}else y.unverifiedPositions.push(o);y.positionsUnresolved++,A.add(s)}else delete n[e],R(o.kind),y.removedPositions.push(o)}return y.deadSpaceIds=[...A].sort((e,t)=>e.localeCompare(t)),{config:s,layout:n,report:y,changed:JSON.stringify(s)!==o||JSON.stringify(n)!==a}}(e,t,i),a=o.config,r=cl(a,o.layout);pl(a),dl(o.layout);const l=JSON.stringify(e||{}),c=JSON.stringify(t||{}),h=Number.isInteger(Number(a.model_version))?Number(a.model_version):0,d=(e=>{let t=0,i=0,s=0;for(const i of e.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",t++),Array.isArray(i.controls)){const e="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=e?i.controls.filter(t=>t!==e):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,t++)}const e=i.vacuum;e&&rg(e,"trail")&&(["never","cleaning","always"].includes(e.trail_mode)||(e.trail_mode=!1===e.trail?"never":"cleaning"),delete e.trail,t++)}for(const n of e.spaces||[]){const e=n.settings;"glow"===e?.fill_mode&&("boolean"!=typeof e.glow_enabled&&(e.glow_enabled=!0),e.fill_mode="none",t++,i++);for(const e of n.rooms||[]){const i=e.settings;"glow"===i?.fill_mode&&("boolean"!=typeof i.glow&&(i.glow=!0),delete i.fill_mode,t++,s++)}if(rg(n,"segments")&&(delete n.segments,t++),rg(n,"plan_scale")){const e=Number(n.plan_scale);Number.isFinite(e)&&e>=Mh&&e<=Ch&&(rg(n,"plan_scale_x")||(n.plan_scale_x=e),rg(n,"plan_scale_y")||(n.plan_scale_y=e),delete n.plan_scale,t++)}const o=Number(n.cell_cm);rg(n,"cell_cm")&&(!Number.isFinite(o)||o<.1||o>1e3)&&(n.cell_cm=Number.isFinite(o)&&o>0?lg(o,.1,1e3):5,t++);const a=Number(n.cell_cm),r=Number.isFinite(a)&&a>0?a:5;for(const e of n.wall_columns||[])if("circle"===e.shape)rg(e,"angle")&&(delete e.angle,t++);else if("square"===e.shape&&rg(e,"angle")){const i=((Number(e.angle)||0)%90+90)%90;e.angle!==i&&(e.angle=i,t++)}for(const e of n.decor||[]){if(rg(e,"width")){const i=Number(e.width);(rg(e,"width_cm")||Number.isFinite(i))&&(rg(e,"width_cm")||(e.width_cm=Number(lg(i/zh*r,.1,100).toFixed(6))),delete e.width,t++)}if("rect"!==e?.kind&&"ellipse"!==e?.kind||!0!==e.fill||(rg(e,"fill_color")||(e.fill_color=e.color||"#607d8b",t++),rg(e,"fill_opacity")||(e.fill_opacity=.25,t++)),"text"!==e?.kind)continue;void 0===e.size_cm?(e.size_cm=Number(lg(ts*is(e)/zh*r,.1,2e3).toFixed(6)),delete e.scale,delete e.size,t++):(rg(e,"scale")||rg(e,"size"))&&(delete e.scale,delete e.size,t++);let i=String(e.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(e=>!!Yi(e[1]));if(!(rg(e,"entity")||rg(e,"attr")||rg(e,"unit")))continue;const n=String(e.unit??"").trim(),o="state"===String(e.attr??"").trim().toLowerCase()?null:e.attr,a=s||n?"":Ji(e.entity,o);if(s||a){if(a){const t=i.indexOf("{}");i=t>=0?i.slice(0,t)+a+i.slice(t+2):`${i}${i?" ":""}${a}`,e.text=i}delete e.entity,delete e.attr,delete e.unit,t++}}}return{total:t,glowSpaces:i,glowRooms:s}})(a);let p=d.total;const u=(_=a.spaces||[],JSON.parse(JSON.stringify(_)));var _;const m=sm(a.spaces||[],o.layout);let g=0,f=0,v=0,y="";const b=m.spaces.map(e=>{const t=function(e){const t=JSON.parse(JSON.stringify(e||{}));let i=Array.isArray(t.rooms)?t.rooms:[];const s=new Map;for(const e of i){const t=hi(e);if(t)for(let e=0;ee.key.localeCompare(t.key))){let t=null;for(const s of $a(e,i)){const e=r.get(_a(s.from));if(e&&e!==_a(s.to))continue;const n=wa(i,s);if(ka(i,n,s)){t=s,i=n;break}}t?(r.set(_a(t.from),_a(t.to)),n++,a=Math.max(a,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}t.rooms=i;for(const e of t.room_drafts||[]){if(!Array.isArray(e?.points)||e.points.length<2)continue;const t=e.points.map(e=>[...e]),i=t.slice(0,-1).flatMap((i,s)=>{const n=t[s+1],o=pa(i,n);return o?[{key:`${String(e.id||"")}:${s}`,a:[...i],b:[...n],axis:o.axis}]:[]});let s=t;for(const e of i){let t=null;for(const i of $a(e,[{poly:s}])){const e=s.map(e=>ba(e,i)),n=e.length>=4&&ma(e[0],e[e.length-1]),o=n?e.slice(0,-1):e;if(n?va(o):ya(o)){t=i,s=e;break}}t?(n++,a=Math.max(a,Math.hypot(t.to[0]-t.from[0],t.to[1]-t.from[1]))):o++}e.points=s}for(const e of t.partitions||[]){if(!Array.isArray(e?.a)||!Array.isArray(e?.b))continue;const i=pa(e.a,e.b);if(!i)continue;const s={key:String(e.id||ga(e.a,e.b)),a:[...e.a],b:[...e.b],axis:i.axis};let r=null;for(const i of $a(s,[{poly:[e.a,e.b]}])){const s=ba(e.a,i),n=ba(e.b,i),o=Math.hypot(n[0]-s[0],n[1]-s[1]);if(!(o>0))continue;const a=(t.openings||[]).filter(t=>"partition"===t?.host?.kind&&t.host.id===e.id).every(e=>{const t=Number(e.host.t),i=Number(e.length);return Number.isFinite(t)&&t>=0&&t<=1&&Number.isFinite(i)&&i>0&&t*o-i/2>=-1e-12&&t*o+i/2<=o+1e-12});if(a){r=i,e.a=s,e.b=n;break}}r?(n++,a=Math.max(a,Math.hypot(r.to[0]-r.from[0],r.to[1]-r.from[1]))):o++}return{space:t,report:{wallsStraightened:n,wallsStraightenSkipped:o,maxStraightenShift:a},changed:n>0}}(e);g+=t.report.wallsStraightened,f+=t.report.wallsStraightenSkipped;const i=Number(e?.cell_cm)>0?Number(e.cell_cm):5,s=t.report.maxStraightenShift/Fh*i;return s>v&&(v=s,y=String(e?.id||"")),t.space}),w=g?sm(b,m.layout):{...m,spaces:b};a.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,$=0,S=0,M=0,C=0,D=0,P=0,T=0;for(let e=0;e0===Number(e.cm)),walls:t.walls||[]}),o=cg(t),r=cg(i);if(!o||!r)continue;const l=pg(o.rooms),c=pg(r.rooms),h=.02*zh,d=(t.wall_segments||[]).filter(e=>0===Number(e.cm)).map(e=>({...e,key:Ml(e.a,e.b,Fh),cm:0})),p=new Set(d.map(e=>e.key)),_=w_(t,o.rooms,$h,h);$+=_.length;for(const e of _){const t=[e[0]/$h,e[1]/$h],i=[e[2]/$h,e[3]/$h],s=Ml(t,i,Fh);p.has(s)||(p.add(s),d.push({key:s,a:t,b:i,cm:0}))}const m=d.length,g=Ol(d,l,c,Fh,$h),f=(e,t,i)=>{const s=i[0]-t[0],n=i[1]-t[1],o=s*s+n*n;if(!(o>0))return Math.hypot(e[0]-t[0],e[1]-t[1]);const a=Math.max(0,Math.min(1,((e[0]-t[0])*s+(e[1]-t[1])*n)/o));return Math.hypot(e[0]-(t[0]+s*a),e[1]-(t[1]+n*a))},v=g.filter(e=>{if(!Array.isArray(e.a)||!Array.isArray(e.b))return!1;const t=e.a.map(e=>e*$h),i=e.b.map(e=>e*$h);return c.some(([e,s])=>f(t,e,s)<=4*h&&f(i,e,s)<=4*h)});S+=Math.max(0,m-v.length);const y=(i.wall_segments||[]).filter(e=>Number(e.cm)>0);i.wall_segments=[...y,...v.map(({key:e,...t})=>t)];const b=v.map(e=>[e.a[0]*$h,e.a[1]*$h,e.b[0]*$h,e.b[1]*$h]),w=Array.isArray(t.walls)?t.walls.length:0;let k=Ol(t.walls,l,c,Fh,$h);k=dg(r.rooms,k,b,Fh,Number(i.cell_cm)>0?Number(i.cell_cm):5,zh,$h),k=vc(r.rooms,k,b,Fh,Number(i.cell_cm)>0?Number(i.cell_cm):5,zh,$h),k=Fl(k,i.rooms||[],Fh,1,b.map(e=>[e[0]/$h,e[1]/$h,e[2]/$h,e[3]/$h])),x+=Math.max(0,w-k.length),k.length?i.walls=k:delete i.walls;const I=hm(i.partitions||[],{pitch:Fh,geometry:pm(i)});I.merged&&(M+=I.merged,i.partitions=I.partitions,dm(i.openings,i.partitions,I.openingMoves,{coordScale:$h,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:zh}));const R=cg(i);if(R){const e=n(i,R,i.walls||[],b,{pitch:Fh,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,gridPitch:zh,coordScale:$h});e.partitionsReconciled&&(C+=e.partitionsReconciled,D+=e.openingsRehosted,e.partitions.length?i.partitions=e.partitions:delete i.partitions,e.openings.length?i.openings=e.openings:delete i.openings,e.walls.length?i.walls=e.walls:delete i.walls),e.removedDrafts&&(P+=e.removedDrafts,e.roomDrafts.length?i.room_drafts=e.roomDrafts:delete i.room_drafts)}JSON.stringify({zero:(i.wall_segments||[]).filter(e=>0===Number(e.cm)),walls:i.walls||[]})!==s&&T++}let I=0;h<=9&&(I=function(e){const t=ng(e);return ag(e,t.config),{...t,config:e}}(a).migratedSegments);const R=pl(a),A=dl(w.layout),z=JSON.stringify(R)!==l||JSON.stringify(A)!==c;h<9&&z&&(R.model_version=9);const F=JSON.stringify(R)!==l||JSON.stringify(A)!==c,E=Number.isInteger(Number(R.model_version))?Number(R.model_version):h,N=F?k:{...k,moved:0,coordsCanonicalized:0,maxShift:0,maxShiftCm:0,maxSpace:"",rotated:0,removedDrafts:0},O=F?o.report:{...o.report,spaceRefsRemapped:0,roomRefsRemapped:0,positionsRemapped:0,markersDetached:0,orphanRoomLabelsRemoved:0,orphanDevicePositionsRemoved:0,orphanGroupPositionsRemoved:0,liveMissingPositionsRemoved:0};return{config:R,layout:A,report:{...N,modelFrom:h,modelTo:E,migrated:F?p:0,glowSpacesMigrated:F?d.glowSpaces:0,glowRoomsMigrated:F?d.glowRooms:0,canonicalized:F?T:0,wallSegmentsMigrated:F?I:0,legacyZeroWallsMigrated:F?$:0,wallsMerged:F?x:0,spansMerged:F?S:0,partitionsMerged:F?M:0,partitionsReconciled:F?C:0,openingsRehosted:F?D:0,redundantDraftsRemoved:F?P:0,wallsStraightened:F?g:0,wallsStraightenSkipped:f,maxStraightenShiftCm:F?v:0,maxStraightenSpace:F?y:"",latticeCoordinatesCanonicalized:F?r.canonicalized:0,latticeCoordinatesFar:F?r.far:0,latticeMaxShift:F?r.maxShift:0,latticeMaxShiftCm:F?r.maxShiftCm:0,latticeSpaces:F?r.spaces:[],...O},changed:F}}function _g(e,t,i){const s=(e?.spaces||[]).find(e=>e?.id===i),n=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),o=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===i||"string"==typeof e.room_id&&n.has(e.room_id)||t?.[e.id]?.s===i).map(e=>e.id))].sort((e,t)=>e.localeCompare(t));return{markerIds:o,count:o.length}}function mg(e){return e instanceof Error?e.name||"Error":typeof e}function gg(e){return Md({id:e?.id??"",cell_cm:e?.cell_cm,rooms:e?.rooms||[],walls:e?.walls||[],wall_segments:e?.wall_segments||[],open_spans:e?.open_spans||[],openings:e?.openings||[],partitions:e?.partitions||[],room_drafts:e?.room_drafts||[],wall_columns:e?.wall_columns||[]})}function fg(e,t,i,s=zh,n=1e3){return(Array.isArray(e?.openings)?e.openings:[]).flatMap(e=>{const o={...e,rx:Number(e.x)*n,ry:Number(e.y)*n,rlen:Number(e.length)*n};if(!e.host||"wall"===e.host.kind)return[o];const a=Tp(e,t.partitions,n,i,s);return a.resolved?[{...e,rx:a.resolved.center[0],ry:a.resolved.center[1],rlen:a.resolved.length,angle:a.resolved.angle,partitionHost:a.resolved}]:[]})}function vg(e,t=()=>!0){return e.flatMap(e=>e.host&&e.partitionHost&&t(e)?[Ap(e.partitionHost)]:[])}function yg(e,t,i,s,n,o,a=zh,r=1e3){const l=gc(t.rooms,[...i],s,n,o,a,r);return e.flatMap(e=>{const t={x:e.rx,y:e.ry,angle:Number(e.angle)||0,length:e.rlen};return e.host&&"wall"!==e.host.kind?e.partitionHost&&Ep(e.partitionHost,l,2e-4*a)?[t]:[]:[t]})}function bg(e,t){const i=Fh,s=zh,n=$h,o=Number.isFinite(Number(t.cellCm))&&Number(t.cellCm)>0?Number(t.cellCm):5,a=Array.isArray(e?.walls)?e.walls:[],r=function(e,t,i=zh,s=1e3){return yd(t.rooms,e?.open_spans,s,.02*i)}(e,t,s,n),l=function(e,t,i=zh){if(!t.length)return[];const s=e.filter(e=>e?.id),n=.02*i,o=[];for(let e=0;e{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return h.some(e=>Ls(t,e)<4*n)}).map(e=>[...e]);d.length&&o.push({a:a,b:r,segs:d})}return o}(t.rooms,r,s).flatMap(e=>e.segs),c=fg(e,t,o,s,n),h=vg(c),d=rh(t,o,s,2e-4*s,h).all;return{space:t,walls:a,openCuts:l,openings:c,roomOpenings:yg(c,t,a,l,i,o,s,n),partitionCuts:h,physicalBodies:d,wallKeyPitch:i,cellCm:o,gridPitch:s,coordScale:n}}function wg(e,t,i){const s="string"==typeof e?.title?e.title.trim():"",n=null==e?.id?"":String(e.id).trim();return{spaceId:n,displayName:s||n||i(t+1)}}function kg(e,t={}){const i=(t.fingerprint||Md)(e),s=Array.isArray(e?.spaces)?e.spaces:[],n=t.fallbackSpaceName||(e=>`Space ${e}`),o=t.prepareSpace||bg,a=t.wallPass||Ec,r=t.floorPass||zc,l=[];for(let i=0;i0||d.physicalBodies.length>0;if(!d.space.rooms.length&&!p){l.push({...h,status:"not-applicable"});continue}let u=null;if(p){try{u=a(d.space.rooms,d.walls,d.openCuts,d.roomOpenings,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale,d.physicalBodies)}catch(e){l.push({...h,status:"failed",reason:"wall-exception",detail:mg(e)});continue}if(null==u){l.push({...h,status:"failed",reason:"wall-null"});continue}if("degraded-extra"===u.status){l.push({...h,status:"failed",reason:"wall-degraded-extra"});continue}if("failed-core"===u.status){l.push({...h,status:"failed",reason:"wall-failed-core"});continue}t.captureWallGeometry?.(d,u)}if(d.space.rooms.length&&null==u?.paperGeom){let e;try{e=r(d.space.rooms,d.walls,d.openCuts,d.wallKeyPitch,d.cellCm,d.gridPitch,d.coordScale)}catch(e){l.push({...h,status:"failed",reason:"floor-exception",detail:mg(e)});continue}if(null==e){l.push({...h,status:"failed",reason:"floor-null"});continue}}l.push({...h,status:"ok"})}const c=l.filter(e=>"failed"===e.status);return{fingerprint:i,spaces:l,failures:c,ok:0===c.length}}class xg{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}clear(){this._undo=[],this._redo=[]}}const $g=new WeakMap;const Sg=(e,t,i)=>255===e[i+3]&&t.every((t,s)=>Math.abs(e[i+s]-t)<=2);async function Mg(e){const t=e.defaultView;if(!t||!t.CSS?.supports?.("mix-blend-mode","screen"))return!1;const i=[128,32,16],s=[16,64,128],n=[9,19,29],o=`\n \n \n \n \n \n `,a=t.URL.createObjectURL(new Blob([o],{type:"image/svg+xml"}));try{const o=await new Promise((e,i)=>{const s=new t.Image,n=t.setTimeout(()=>i(new Error("SVG blend probe timeout")),1e3);s.onload=()=>{t.clearTimeout(n),e(s)},s.onerror=()=>{t.clearTimeout(n),i(new Error("SVG blend probe failed"))},s.src=a}),r=e.createElement("canvas");r.width=4,r.height=1;const l=r.getContext("2d",{willReadFrequently:!0});if(!l)return!1;l.drawImage(o,0,0,4,1);const c=l.getImageData(0,0,4,1).data;return Sg(c,i,0)&&Sg(c,function(e,t){return[0,1,2].map(i=>Math.round(255-(255-e[i])*(255-t[i])/255))}(i,s),4)&&Sg(c,s,8)&&Sg(c,n,12)}catch{return!1}finally{t.URL.revokeObjectURL(a)}}const Cg={now:()=>performance.now(),requestFrame:e=>requestAnimationFrame(e),cancelFrame:e=>cancelAnimationFrame(e)},Dg=e=>Math.max(0,Math.min(1,e)),Pg=(e,t,i)=>e+(t-e)*i,Tg=(e,t,i)=>{const s=1-e;return 3*s*s*e*t+3*s*e*e*i+e*e*e};function Ig(e,t){return{centerX:e.x+e.w/2,centerY:e.y+e.h/2,pixelsPerUnit:t>0&&e.w>0?t/e.w:1,viewBox:{...e}}}function Rg(e,t,i){const s=Math.max(1e-9,e.pixelsPerUnit),n=Math.max(1e-9,t/s),o=Math.max(1e-9,i/s);return{x:e.centerX-n/2,y:e.centerY-o/2,w:n,h:o}}function Ag(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 s=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(t)?.[1];return s?[parseInt(s.slice(0,2),16),parseInt(s.slice(2,4),16),parseInt(s.slice(4,6),16),8===s.length?parseInt(s.slice(6,8),16)/255:1]:null}function zg(e,t,i){const s=Ag(e),n=Ag(t);if(!s||!n)return i<.5?e:t;const o=Dg(i);return`rgba(${Math.round(Pg(s[0],n[0],o))}, ${Math.round(Pg(s[1],n[1],o))}, ${Math.round(Pg(s[2],n[2],o))}, ${Pg(s[3],n[3],o).toFixed(3)})`}function Fg(e,t,i){const s=(e=>{const t=Dg(e);if(0===t||1===t)return t;let i=0,s=1,n=t;for(let e=0;e<12;e++)n=(i+s)/2,Tg(n,.2,.2){const t=this._state;if(!t||t.token!==n)return;const i=Dg((e-t.startedAt)/t.duration);t.presented=i>=1?t.to:Fg(t.from,t.to,i),t.phase=i>=1?"settling":"running",this._hooks.frame(t),i<1?this._raf=this._clock.requestFrame(a):(this._raf=0,this._hooks.settled(t),this._state?.token===n&&(this._state=null))};return this._raf=this._clock.requestFrame(a),n}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 Ng="houseplan_card_labs_v1",Og=Object.freeze([Object.freeze({id:"iso",issue:89,since:"1.62.0",expires:"1.65.0",summary:"Volumetric plan renderer"})]);function Lg(e){const t=/^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/.exec(String(e||"").trim());if(!t)return null;const i=t.slice(1).map(Number);return i.every(e=>Number.isSafeInteger(e))?i:null}function Hg(e,t){for(let i=0;i<3;i++)if(e[i]!==t[i])return e[i]e.split(",")).map(e=>e.trim()).filter(Boolean)}function Gg(e,t,i,s=Og){const n=function(e,t=Og){const i=Lg(e),s=new Map;if(!i||!qg(t))return s;for(const e of t){const t=Lg(e.since),n=Lg(e.expires);Hg(i,t)>=0&&Hg(i,n)<0&&s.set(e.id,e)}return s}(i,s),o=new Set(qg(s)?s.map(e=>e.id):[]),a=new Set(function(e){if(!e)return[];try{const t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>"string"==typeof e):[]}catch{return[]}}(t).filter(e=>n.has(e)));let r=!1;const l=e=>{if("off"===e)return a.clear(),void(r=!0);const t=e.startsWith("-"),i=t?e.slice(1):e;o.has(i)&&(r=!0,t||!n.has(i)?a.delete(i):a.add(i))};for(const t of Ug(new URLSearchParams(String(e.search||"").replace(/^\?/,""))))l(t);for(const t of Ug(Wg(e.hash)))l(t);const c=Object.freeze([...a].sort());return{active:c,space:jg(e.hash),persist:r?JSON.stringify(c):void 0,knownUrlOperation:r}}let Vg={active:Object.freeze([]),space:""},Kg="",Yg=!1,Jg="";const Xg=new Set;function Zg(){if("undefined"==typeof window)return Vg;const e=Gg(window.location,function(){try{return window.localStorage.getItem(Ng)}catch{return null}}(),Kg);void 0!==e.persist&&function(e){try{window.localStorage.setItem(Ng,e)}catch{}}(e.persist),Vg={active:e.active,space:e.space},window.__hpLabs=e.active;for(const e of Xg)e(Vg);return Vg}function Qg(){Zg()}function ef(e){return Kg=e,"undefined"==typeof window?Vg:(Yg||(Yg=!0,window.addEventListener("hashchange",Qg),window.addEventListener("popstate",Qg)),Zg())}const tf=Object.freeze({rotDeg:0,tiltDeg:20,xyScale:1,zScale:1,origin:Object.freeze([500,500])});function sf(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 nf(e,t,i=tf){if(!(sf(i)&&Number.isFinite(e[0])&&Number.isFinite(e[1])&&Number.isFinite(t)))throw new Error("invalid isometric projection input");const s=i.rotDeg*Math.PI/180,n=i.tiltDeg*Math.PI/180,o=e[0]-i.origin[0],a=e[1]-i.origin[1],r=(o*Math.cos(s)-a*Math.sin(s))*i.xyScale,l=(o*Math.sin(s)+a*Math.cos(s))*i.xyScale;return[i.origin[0]+r,i.origin[1]+l*Math.cos(n)-t*i.zScale*Math.sin(n)]}function of(e,t=tf){if(!sf(t)||!Number.isFinite(e[0])||!Number.isFinite(e[1]))throw new Error("invalid isometric projection input");const i=t.rotDeg*Math.PI/180,s=t.tiltDeg*Math.PI/180,n=(e[0]-t.origin[0])/t.xyScale,o=(e[1]-t.origin[1])/(t.xyScale*Math.cos(s));return[t.origin[0]+n*Math.cos(i)+o*Math.sin(i),t.origin[1]-n*Math.sin(i)+o*Math.cos(i)]}function af(e=tf){return`matrix(${function(e=tf){if(!sf(e))throw new Error("invalid isometric camera");const t=e.rotDeg*Math.PI/180,i=e.tiltDeg*Math.PI/180,s=e.xyScale*Math.cos(t),n=-e.xyScale*Math.sin(t),o=e.xyScale*Math.sin(t)*Math.cos(i),a=e.xyScale*Math.cos(t)*Math.cos(i);return[s,o,n,a,e.origin[0]-s*e.origin[0]-n*e.origin[1],e.origin[1]-o*e.origin[0]-a*e.origin[1]]}(e).map(e=>Number(e.toFixed(12))).join(" ")})`}function rf(e,t=tf){const{rect:i,wallHeight:s}=e,n=e.openingHeight??s,o=e.floorDepth??0;if(!(i.w>=0)||!(i.h>=0)||!Number.isFinite(s)||!Number.isFinite(n)||!Number.isFinite(o)||s<0||n<0||o<0)throw new Error("invalid isometric frame");const a=[[i.x,i.y],[i.x+i.w,i.y],[i.x+i.w,i.y+i.h],[i.x,i.y+i.h]],r=Math.max(s,n),l=a.flatMap(e=>[nf(e,-o,t),nf(e,r,t)]),c=l.map(e=>e[0]),h=l.map(e=>e[1]),d=Math.min(...c),p=Math.min(...h);return{x:d,y:p,w:Math.max(...c)-d,h:Math.max(...h)-p}}const lf=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function cf(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 s=!t;return cf(i)>0===s?i:[...i].reverse()}function df(e,t,i){return e.length?`M ${e.map(e=>lf(nf(e,i,t))).join(" L ")} Z`:""}function pf(e,t,i){const s=t[1]-e[1],n=-(t[0]-e[0]),o=i.rotDeg*Math.PI/180;return s*Math.sin(o)+n*Math.cos(o)}function uf(e,t,i=tf){if(!Number.isFinite(t)||t<0)throw new Error("invalid floor edge height");const s=(e||[]).map(e=>(e||[]).map((e,t)=>function(e,t){const i=hf(e,t);if(!i.length)return i;let s=0;for(let e=1;e0)).filter(e=>e.length>=3&&Math.abs(cf(e))>=1e-9)).filter(e=>e.length&&cf(e[0])>0);s.sort((e,t)=>{const i=e[0][0],s=t[0][0];return i[0]-s[0]||i[1]-s[1]||Math.abs(cf(t[0]))-Math.abs(cf(e[0]))});const n=[],o=[];let a=0;for(let e=0;ee.depth-t.depth||e.component-t.component||e.edge-t.edge),{footprintPath:n.join(" "),sides:o,componentCount:s.length,edgeCount:a}}function _f(e,t){return e^=Math.round(64*(Number.isFinite(t)?t:0)),Math.imul(e,16777619)>>>0}const mf=e=>e.every(Number.isFinite);function gf(e,t,i,s){const n=e[0]*i,o=e[1]*s,a=t*Math.PI/180;return[n*Math.cos(a)-o*Math.sin(a),n*Math.sin(a)+o*Math.cos(a)]}function ff(e,t,i,s,n,o,a){const r=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],gf(i,e.angle,r,l));return{leaf:t,hinge:h,closedVector:gf(s,e.angle,r,l),quarterVector:gf([-s[1],s[0]],e.angle,r,l),turnDeg:n,bottom:o,top:a}}const vf=e=>`${Number(e[0].toFixed(4))} ${Number(e[1].toFixed(4))}`;function yf(e,t,i=tf){const s=Math.max(0,Math.min(1,Number.isFinite(t)?t:0));return e.leaves.map(t=>{const n=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,s),o=nf(t.hinge,t.bottom,i),a=nf(n,t.bottom,i),r=nf(n,t.top,i),l=nf(t.hinge,t.top,i);return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaf:t.leaf,d:`M ${vf(o)} L ${vf(a)} L ${vf(r)} L ${vf(l)} Z`,shadowD:`M ${vf(nf(t.hinge,0,i))} L ${vf(nf(n,0,i))}`,depth:Math.max(o[1],a[1])}})}const bf={window:120,door:90,passage:90,gate:300};function wf(e){return bf[e]}function kf(e,t){const i=[e[0],e[1]],s=[t[0],t[1]];return function(e,t){return e[0]-t[0]||e[1]-t[1]}(i,s)<=0?{a:i,b:s}:{a:s,b:i}}function xf(e){const t=new Map;return e.forEach((e,i)=>{if(!e.kind||e.open)return;const s=kf(e.a,e.b);if(Math.hypot(s.b[0]-s.a[0],s.b[1]-s.a[1])<=1e-9)return;const n=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])}`}(s.a,s.b),o=t.get(n);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(n,{segmentKey:n,a:s.a,b:s.b,physicalHalfWidth:Math.max(0,e.half||0),sourceOrder:i,...e.partitionHost?{partitionHost:e.partitionHost}:{}})}),[...t.values()]}function $f(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 Sf(e){const t=xf(e.intervals).map(t=>{const i=function(e,t){const i=t.b[0]-t.a[0],s=t.b[1]-t.a[1],n=Math.hypot(i,s),o=i/n,a=s/n,r=(e[0]-t.a[0])*o+(e[1]-t.a[1])*a,l=Math.max(0,Math.min(n,r)),c=t.a[0]+l*o,h=t.a[1]+l*a;return{along:l,length:n,x:c,y:h,distance:Math.hypot(e[0]-c,e[1]-h),perpendicular:Math.abs((e[0]-t.a[0])*a-(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,s,n){const o=t.b[0]-t.a[0],a=t.b[1]-t.a[1],r=Math.hypot(o,a);if(!(r>1e-9))return!1;const l=o/r,c=a/r;for(const o of i){if(!o.open&&o.kind)continue;const i=kf(o.a,o.b),a=i.b[0]-i.a[0],r=i.b[1]-i.a[1],h=Math.hypot(a,r);if(!(h>1e-9))continue;const d=a/h,p=r/h;if(Math.abs(l*p-c*d)>1e-6)continue;if(Math.abs((i.a[0]-t.a[0])*c-(i.a[1]-t.a[1])*l)>n)continue;const u=(e[0]-i.a[0])*d+(e[1]-i.a[1])*p,_=1e-9;if(!(u<=_||u>=h-_)&&Math.abs((e[0]-i.a[0])*p-(e[1]-i.a[1])*d)<=s+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($f),s=t.filter(t=>!t.target.partitionHost||e.renderedLength+2*t.target.physicalHalfWidth<=t.length+1e-9).sort($f);let n=s[0];if(!n)return{candidate:null,jambBlockedTarget:i[0]?.target||null};const o=s.filter(e=>Math.abs(e.distance-n.distance)<=1e-9&&Math.abs(e.perpendicular-n.perpendicular)<=1e-9),a=o.filter(e=>e.target.partitionHost);if(a.length){if(1!==new Set(a.map(e=>e.target.partitionHost.id)).size||a.some(e=>e.target.ambiguousPartitionHost))return{candidate:null,jambBlockedTarget:null};const e=a[0],t=e.target.b[0]-e.target.a[0],i=e.target.b[1]-e.target.a[1],s=Math.hypot(t,i),r=t/s,l=i/s,c=o.every(t=>{const i=t.target.b[0]-t.target.a[0],s=t.target.b[1]-t.target.a[1],n=Math.hypot(i,s),o=i/n,a=s/n,c=Math.abs(r*a-l*o)<=1e-6,h=Math.abs((t.target.a[0]-e.target.a[0])*l-(t.target.a[1]-e.target.a[1])*r);return c&&h<=1e-9});if(!c)return{candidate:null,jambBlockedTarget:null};n=e}if(n.target.ambiguousPartitionHost)return{candidate:null,jambBlockedTarget:null};const{target:r,length:l}=n,c=r.b[0]-r.a[0],h=r.b[1]-r.a[1],d=c/l,p=h/l,u=r.partitionHost?r.physicalHalfWidth:0,_=Math.min(Math.max(0,e.renderedLength)/2+u,l/2);let m=n.along;const g=Math.max(e.gridStep,1e-9),f=l/2;m=Math.abs(m-f)<=g/2?f:Math.round(m/g)*g,m=Math.max(_,Math.min(l-_,m));const v=r.a[0]+d*m,y=r.a[1]+p*m,b=Math.max(0,e.renderedLength)/2,w=m-b,k=m+b,x=Math.max(0,w),$=Math.max(0,l-k),S=[r.a[0]+d*(w-x/2),r.a[1]+p*(w-x/2)],M=[r.a[0]+d*(k+$/2),r.a[1]+p*(k+$/2)];let C=180*Math.atan2(h,c)/Math.PI;C>=90?C-=180:C<-90&&(C+=180);const D=Math.abs(m-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:r,...r.partitionHost?{host:{...r.partitionHost,t:l>0?m/l:0}}:{},measure:{labels:[{distance:x,midpoint:S},{distance:$,midpoint:M}],guide:D?{x:v,y:y,angle:C}:null}},jambBlockedTarget:null}}function Mf(e,t,i,s,n=1e-6){return e.presetRevision===i&&e.geometryRevision===s&&Math.hypot(e.pointer[0]-t[0],e.pointer[1]-t[1])<=n}const Cf=(e,t)=>e[0]*t[1]-e[1]*t[0],Df=(e,t)=>e[0]*t[0]+e[1]*t[1],Pf=(e,t)=>[e[0]-t[0],e[1]-t[1]],Tf=(e,t,i)=>[e[0]+t[0]*i,e[1]+t[1]*i];function If(e,t,i){return Math.abs(Cf(e,t))<=i}function Rf(e,t,i){return Math.abs(Cf(Pf(e,t),i))}function Af(e,t,i,s,n){const o=t[0]-e[0],a=t[1]-e[1],r=Math.hypot(o,a);if(!(r>n&&i>0))return[];const l=[o/r,a/r];return function(e,t,i){const s=t.map(([t,i])=>[Math.max(0,Math.min(t,i)),Math.min(e,Math.max(t,i))]).filter(([e,t])=>t>e+i).sort((e,t)=>e[0]-t[0]||e[1]-t[1]),n=[];for(const e of s){const t=n[n.length-1];!t||e[0]>t[1]+i?n.push([...e]):t[1]=Math.max(t[1],e[1])}const o=[];let a=0;for(const[e,t]of n)e>a+i&&o.push([a,e]),a=Math.max(a,t);return e>a+i&&o.push([a,e]),o}(r,s.flatMap(t=>{const s=t.b[0]-t.a[0],o=t.b[1]-t.a[1],a=Math.hypot(s,o);if(!(a>n&&If(l,[s/a,o/a],1e-6)))return[];if(Rf(t.a,e,l)>1.1*Math.max(i,n)||Rf(t.b,e,l)>1.1*Math.max(i,n))return[];return[[Df(Pf(t.a,e),l),Df(Pf(t.b,e),l)]]}),n).flatMap(([t,s])=>{const n=Wl({a:Tf(e,l,t),b:Tf(e,l,s),halfDepth:i});return n?[n]:[]})}function zf(e){const t=e.coordScale??1,i=Math.max(e.epsilon??2e-4*e.gridPitch,1e-9),s=[];for(const i of e.rooms||[]){if(!i?.id)continue;const n=mc(e.rooms,i.id,e.walls,e.openCuts,e.pitch,e.cellCm,e.gridPitch,t);if(!n||n.poly.length<3)continue;const o=sc(n.poly,n.offsets);!o||o.length<3||s.push({roomId:i.id,profile:n,inner:o})}s.sort((e,t)=>e.roomId.localeCompare(t.roomId));const n=(e.roomOpenings||[]).map(e=>{const t=e.angle*Math.PI/180,i=[Math.cos(t),Math.sin(t)],s=Math.max(0,e.length)/2;return{a:Tf([e.x,e.y],i,-s),b:Tf([e.x,e.y],i,s)}}),o=[];for(const s of Lc(e.rooms,e.walls,e.openCuts,e.pitch,e.cellCm,e.gridPitch,t))for(const e of Af(s.a,s.b,s.depthUnits/2,n,i))o.push({body:e});const a=new Map;for(const t of e.partitionCuts||[]){const e=a.get(t.hostId)||[];e.push(t),a.set(t.hostId,e)}for(const t of e.partitions||[]){const s=wl(t.cm,e.cellCm,e.gridPitch)/2;for(const e of Af(t.a,t.b,s,a.get(t.id)||[],i))o.push({body:e,partitionId:t.id})}return{rooms:s,boundaries:o,epsilon:i}}function Ff(e,t,i,s){const n=e.profile.offsets[t];if(!(n>0))return null;const o=function(e,t){const i=e[t],s=e[(t+1)%e.length],n=s[0]-i[0],o=s[1]-i[1],a=Math.hypot(n,o)||1;return function(e){let t=0;for(let i=0;i=0?[-o/a,n/a]:[o/a,-n/a]}(e.profile.poly,t),a=Tf(i.center,o,n),r=[];for(let t=0;ts&&If(i.axis,[l[0]/c,l[1]/c],1e-6)))continue;if(Rf(n,a,i.axis)>s||Rf(o,a,i.axis)>s)continue;const h=Df(Pf(n,a),i.axis),d=Df(Pf(o,a),i.axis);r.push([Math.min(h,d),Math.max(h,d)])}const l=function(e,t){e.sort((e,t)=>e[0]-t[0]||e[1]-t[1]);const i=[];for(const s of e){const e=i[i.length-1];!e||s[0]>e[1]+t?i.push([...s]):e[1]=Math.max(e[1],s[1])}return i}(r,s).find(([e,t])=>e<=s&&t>=-s);if(!l)return null;const[c,h]=l,d=Tf(a,i.axis,-i.half),p=Tf(a,i.axis,i.half),u=Math.max(0,-i.half-c),_=Math.max(0,h-i.half),m=u>0?Tf(a,i.axis,c):d,g=_>0?Tf(a,i.axis,h):p,f=Df(o,i.normal)>=0?1:-1,v=(t,s,n)=>({from:t,to:s,label:[(t[0]+s[0])/2,(t[1]+s[1])/2],axis:i.axis,labelNormal:o,distance:n,roomId:e.roomId,roomSide:f,source:"room-face"});return{left:v(d,m,u),right:v(p,g,_)}}function Ef(e){const t=Tf(e.center,e.axis,-e.half),i=Tf(e.center,e.axis,e.half),s=Tf(e.center,e.axis,Math.min(-e.half,e.targetLo)),n=Tf(e.center,e.axis,Math.max(e.half,e.targetHi)),o=(t,i)=>({from:t,to:i,label:[(t[0]+i[0])/2,(t[1]+i[1])/2],axis:e.axis,labelNormal:e.normal,distance:Math.hypot(i[0]-t[0],i[1]-t[1]),source:"host-end"});return[o(t,s),o(i,n)]}function Nf(e,t,i,s){const n=Pf(i,t),o=Df(n,n);if(!(o>s*s))return Math.hypot(...Pf(e,t))<=s;const a=Df(Pf(e,t),n)/o;if(a<-s||a>1+s)return!1;const r=Tf(t,n,Math.max(0,Math.min(1,a)));return Math.hypot(...Pf(e,r))<=s}function Of(e,t,i){let s=!1;for(let n=0,o=t.length-1;ne[1]!=c>e[1]&&e[0]<(l-a)*(e[1]-r)/(c-r)+a&&(s=!s)}return s}function Lf(e,t,i,s,n,o){const a=Pf(s,i),r=Cf(t,a);if(Math.abs(r)<=o)return null;const l=Pf(i,e),c=Cf(l,a)/r,h=Cf(l,t)/r;return c<-o||c>n+o||h<-o||h>1+o?null:Math.max(0,Math.min(n,c))}function Hf(e,t,i){const s=Ef(i),n="partition"===e.host?.kind?e.host.id:void 0;return[{base:s[0],direction:[-i.axis[0],-i.axis[1]]},{base:s[1],direction:i.axis}].map(({base:e,direction:i})=>{const s=e.distance,o=function(e,t,i,s,n,o){let a=null;for(const r of s)if(!n||r.partitionId!==n){if(Of(e,r.body,o))return 0;for(let s=0;sDf(Pf(e,n),i));return{center:n,axis:i,normal:s,half:Math.max(0,e.renderedLength)/2,targetLo:Math.min(...o),targetHi:Math.max(...o)}}(e),s=t.rooms.flatMap(e=>{const s=function(e,t,i){const{profile:s}=e;let n=null;for(let e=0;e0))continue;const o=s.poly[e],a=s.poly[(e+1)%s.poly.length],r=Pf(a,o),l=Math.hypot(r[0],r[1]);if(!(l>i&&If(t.axis,[r[0]/l,r[1]/l],1e-6)))continue;if(Rf(o,t.center,t.axis)>i||Rf(a,t.center,t.axis)>i)continue;const c=Df(Pf(o,t.center),t.axis),h=Df(Pf(a,t.center),t.axis),d=Math.min(c,h),p=Math.max(c,h);if(d>-t.half+i||p0?d:p<0?-p:0);(!n||u2)return Ef(i);if(s.length){const e=s.map(({room:e,edge:s})=>Ff(e,s,i,t.epsilon));if(e.some(e=>!e))return Ef(i);const n=e;return[...n.map(e=>e.left),...n.map(e=>e.right)]}return Hf(e,t,i)}function qf(e){return!(!e.canEdit||e.kiosk||e.fixedFloor)&&("view"!==e.mode&&("mouse"===e.pointerType&&e.spaceCount>1))}const Wf="1.68.1",jf={badge:"display.badge",icon_ripple:"display.icon_ripple",value:"display.value",static_icon:"display.static_icon"},Uf={badge:"marker.display_hint_badge",icon_ripple:"marker.display_hint_icon_ripple",value:"marker.display_hint_value",static_icon:"marker.display_hint_static_icon"},Gf=2e3,Vf=1e3,Kf=1500,Yf=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},Jf=(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}},Xf=(e,t,i,s)=>{for(e.delete(t),e.set(t,i);e.size>s;){const t=e.keys().next().value;if(void 0===t)break;e.delete(t)}},Zf=new Map;let Qf=0;const ev=e=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(e??{})}`;let tv=1e4;const iv="houseplan_card_layout_v1",sv="houseplan_card_cfg_v1",nv="houseplan_card_zoom_v1",ov="houseplan_card_nav_v1",av="houseplan_card_kiosk_v1",rv="houseplan_card_view_v1",lv="hp-dialog, hp-help, hp-color-opacity, hp-device-preview",cv=1e3,hv=[[0,1],[45,.88],[70,.62],[86,.32],[100,0]],dv=(e,t)=>{const i=Math.min(e.x,t.x),s=Math.min(e.y,t.y);return{x:i,y:s,w:Math.max(e.x+e.w,t.x+t.w)-i,h:Math.max(e.y+e.h,t.y+t.h)-s}},pv=new Set(["select","draw","column","merge","split","resize","opening","wallthick","delroom"]),uv=2e3,_v=(e,t,i)=>{const s=new Event(t,{bubbles:!0,composed:!0});s.detail=i??{},e.dispatchEvent(s)},mv=e=>{history.pushState(null,"",e),_v(window,"location-changed",{replace:!1})},gv=(e,t)=>{let i,s=null;const n=(...n)=>{clearTimeout(i),s=n,i=window.setTimeout(()=>{i=void 0;const t=s;s=null,t&&e(...t)},t)};return n.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const t=s;s=null,t&&e(...t)},n.cancel=()=>{clearTimeout(i),i=void 0,s=null},n.pending=()=>void 0!==i,n},fv=e=>{try{e.target?.setPointerCapture?.(e.pointerId)}catch{}};class vv extends he{constructor(){super(...arguments),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=mn(null),this._canOptimizeUndo=!1,this._undoKind=null,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._pointerModality=new W_(this,()=>this._syncPointerHoverTargets()),this._devicePressAnimations=new Map,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Er},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._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._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 Eg({frame:e=>this._applyModeTransitionFrame(e),settled:e=>this._settleModeTransition(e)}),this._editorSecondary=new Y_({root:()=>this.renderRoot,requestUpdate:()=>this.requestUpdate(),updateComplete:()=>this.updateComplete,clearTip:()=>{this._tip=null}}),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 xg(50),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._glowClipCache=new Map,this._glowGeometryWarnings=new Set,this._lightBarrierCache=null,this._lightBarrierPool=new Map,this._glowFeatherUnits=null,this._glowRenderedSources=new Map,this._glowLastAppearance=new Map,this._glowEnteringSources=new Set,this._glowEnterRafs=new Map,this._glowFadeTimers=new Map,this._glowFeatherSuspendUntil=0,this._glowFeatherResumeTimer=0,this._glowSourceSeq=0,this._glowScreenBlend=!1,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._resize=new rr,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._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._onboardingShown=!1,this._rulesDialog=null,this._preflightClipboardFallback=null,this._haIntegrationVersion=null,this._alignDialog=null,this._settingsDialog=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._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=++Qf,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._labs={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._clearTransientHover(!0),this._cancelDevicePressFeedback(),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+O_)>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._lastTap=0,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._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._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=An(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,function(e){const t=Cn(e);t&&(Tn(e,t),In(e,t))}(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 Pd(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=gv(()=>{if(this._serverStorage){const e=[...this._dirtyPos];this._dirtyPos.clear();for(const t of e){const e=hl(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._layout=dl(Xr(this._layout)),localStorage.setItem(iv,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._pendingPhysicalWrites=new Map,this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._resize.dragging)return void this._rszCancelDrag();const e=this._geometryHistory.undo();e&&(this._applyGeometryState(e.before,!0)?this._showToast(this._t("history.undone",{name:e.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._resize.dragging)return void this._rszCancelDrag();const e=this._geometryHistory.redo();e&&(this._applyGeometryState(e.after,!0)?this._showToast(this._t("history.redone",{name:e.name})):this._geometryHistory.clear())},this._saveConfigDebounced=gv(()=>{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=()=>{const e=this._physicalDialog,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=Yf(e.cm);if(null==s)return void this._showPhysicalRange("column"===e.kind?Yc:100,"column"===e.kind?1:0);const n=this._imperial?2.54*s:s,o="column"===e.kind?Yc:100,a="column"===e.kind?1:0;if(!Number.isFinite(n)||no)return void this._showPhysicalRange(o,a);if("column"===e.kind){const t=i.wall_columns.find(t=>t.id===e.id);if(!t)return;const s=Yf(e.angle||"0");if("circle"!==e.shape&&(null==s||s<0||s>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===e.shape?{id:e.id,shape:"circle",center:t.center,cm:n}:{id:e.id,shape:"square",center:t.center,cm:n,angle:s};if(i.wall_columns.some(t=>t.id!==e.id&&vh(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const r=this._geometrySnapshot();if("partition"===e.kind){const i=(t.partitions||[]).find(t=>t.id===e.id);if(i&&0===n&&x_(t.openings,{kind:"partition",id:e.id}))return void this._showToast(this._t("toast.zero_wall_opening_conflict"));i&&(i.cm=n)}else if("column"===e.kind){const i=(t.wall_columns||[]).find(t=>t.id===e.id);i&&(i.cm=Xc(n),i.shape="circle"===e.shape?"circle":"square","square"===i.shape?i.angle=Yf(e.angle||"0"):delete i.angle)}else{const i=(t.room_drafts||[]).find(t=>t.id===e.id);i?.segments?.[e.segment||0]&&(i.segments[e.segment||0].cm=n)}const l=this._commitPhysicalGeometry(this._t("history.physical_edit"),r);this._physicalDialog=null,l||this.requestUpdate()},this._deletePhysicalSelection=()=>{const e=this._physicalSel,t=this._curSpaceCfg;if(!e||!t)return;if("draft"===e.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot();if("partition"===e.kind){const i=(t.openings||[]).filter(t=>"partition"===t.host?.kind&&t.host.id===e.id).sort((e,t)=>(e.host?.t||0)-(t.host?.t||0));if(i.length)return void(this._partitionDeleteDialog={id:e.id,openings:i.map(e=>JSON.parse(JSON.stringify(e)))})}const s="partition"===e.kind?"partitions":"column"===e.kind?"wall_columns":"room_drafts";t[s]=(t[s]||[]).filter(t=>t.id!==e.id),t[s].length||delete t[s],this._activeDraftId===e.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._confirmPartitionDelete=()=>{const e=this._partitionDeleteDialog,t=this._curSpaceCfg;if(!e||!t)return;const i=this._geometrySnapshot();t.partitions=(t.partitions||[]).filter(t=>t.id!==e.id),t.partitions.length||delete t.partitions,t.openings=(t.openings||[]).filter(t=>"partition"!==t.host?.kind||t.host.id!==e.id),t.openings.length||delete t.openings,this._partitionDeleteDialog=null,this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._deleteDraftWhole=()=>{const e="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,t=this._curSpaceCfg;if(!e||!t||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();t.room_drafts=(t.room_drafts||[]).filter(t=>t.id!==e),t.room_drafts.length||delete t.room_drafts,this._activeDraftId===e&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._commitPhysicalGeometry(this._t("history.physical_delete"),i)},this._deleteDraftSegment=()=>{const e=this._physicalDialog,t=this._curSpaceCfg;if(!e||"draft"!==e.kind||!t)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(t.room_drafts||[]).findIndex(t=>t.id===e.id);if(i<0)return;const s=t.room_drafts[i],n=Math.max(0,Math.min(s.segments.length-1,e.segment||0)),o=[],a=s.points.slice(0,n+1),r=s.points.slice(n+1);if(a.length>=2&&o.push({id:s.id,points:a,segments:s.segments.slice(0,n)}),r.length>=2&&o.push({id:o.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:r,segments:s.segments.slice(n+1)}),2===o.length&&t.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();t.room_drafts.splice(i,1,...o),t.room_drafts.length||delete t.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=o.length?{kind:"draft",id:o[0].id}:null,this._commitPhysicalGeometry(this._t("history.draft_segment_delete"),l)},this._confirmRoomDelete=e=>{const t=this._roomDeleteDialog,i=this._curSpaceCfg,s=this._spaceModel();if(!t||!i||!s)return;const n=s.rooms.find(e=>e.id===t.roomId);if(!n)return void(this._roomDeleteDialog=null);const o=this._openCuts(),a=Pu(gc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,cv).filter(e=>e.roomId===n.id),s.partitions,this._openingsR.map(e=>({...e,x:e.rx,y:e.ry})),.02*this._gridPitch),r=new Set(a.materialize.filter(e=>!e.reusePartitionId).map(e=>e.interval.key)).size;if(e&&(i.partitions||[]).length+r>uv)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot(),c=fc(s.rooms,this._spaceWalls,o,this._wallKeyPitch,this._cellCm,this._gridPitch,cv);if(e){i.partitions||=[];const e=new Map,t=new Map,s=e=>{const t=(i.wall_segments||[]).find(t=>{const i=[Number(t.a?.[0])*cv,Number(t.a?.[1])*cv],s=[Number(t.b?.[0])*cv,Number(t.b?.[1])*cv];return this._samePt(e.a,i)&&this._samePt(e.b,s)||this._samePt(e.a,s)&&this._samePt(e.b,i)});return"string"==typeof t?.id&&t.id?t.id:null},n=Date.now().toString(36);for(const o of a.materialize){let a=o.reusePartitionId?i.partitions.find(e=>e.id===o.reusePartitionId):t.get(o.interval.key);a||(a={id:s(o.interval)||`partition-room-${n}-${t.size}`,a:[o.interval.a[0]/cv,o.interval.a[1]/cv],b:[o.interval.b[0]/cv,o.interval.b[1]/cv],cm:o.interval.cm},i.partitions.push(a),t.set(o.interval.key,a)),e.set(o.interval.key,a)}for(const t of i.openings||[]){const i=a.openingIntervals.get(t.id),s=i?e.get(i):null,n=this._openingsR.find(e=>e.id===t.id);if(!s||!n)continue;const o={a:[s.a[0]*cv,s.a[1]*cv],b:[s.b[0]*cv,s.b[1]*cv]};t.host={kind:"partition",id:s.id,t:Tu([n.rx,n.ry],o)}}}else if(a.removeOpeningIds.length){const e=new Set(a.removeOpeningIds);i.openings=(i.openings||[]).filter(t=>!e.has(t.id)),i.openings.length||delete i.openings}i.rooms=i.rooms.filter(e=>e.id!==n.id),this._cfgEpoch++;const h=this._normalizeWalls(c,this._openCuts());h.length?i.walls=h:delete i.walls,this._roomDeleteDialog=null,this._commitPhysicalGeometry(this._t(e?"history.delete_room_keep_walls":"history.delete_room_with_walls"),l),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()},this._rebindPartitionOpening=()=>{const e=this._openingDialog;if(!e?.id)return;const t=e.id;this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingRebindId=t,this._openingPreset={type:e.type,lengthCm:e.lengthCm,flipH:e.flipH,flipV:e.flipV,revision:++this._openingPresetRevision},this._openingDialog=null,this._openingHoverCandidate=null)},this._keepClosedAsPartitions=()=>{if(this._wallFaceBatch)return void this._decideWallFace(!1);if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const e=this._curSpaceCfg,t=this._path.slice(0,-1);if((e.partitions||[]).length+t.length>uv)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=iu(t.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul);e.partitions||=[];const n=Date.now().toString(36),o=this._activeDraftId?(e.room_drafts||[]).find(e=>e.id===this._activeDraftId):null,a=this._draftSegmentsForPath(this._path,o,s);for(let i=0;ie.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),this._commitPhysicalGeometry(this._t("history.contour_to_partitions"),i),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const e=this._spaceDialog;if(e)if(e.pickSaved)this._spaceDialog={...e,pickSaved:!1};else{this._spaceDialog={...e,pickSaved:!0,savedBusy:!0};try{const e=await this.hass.callWS({type:"houseplan/plans/list"}),t=this._spaceDialog;t&&(this._spaceDialog={...t,saved:e?.plans||[],savedBusy:!1})}catch(e){const t=this._spaceDialog;t&&(this._spaceDialog={...t,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(e)}))}}},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=br(e);t!==this._dayCycleClockKey&&(this._dayCycleClockKey=t,this.requestUpdate())},this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const e=this._glowRadiusCm,t=this._imperial?Math.round(e/30.48*10)/10:Math.round(e)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:t,bgColor:hs(this._settings,{bgColor:null})||null,northDeg:Pr(this._settings,{}),bgMode:Tr(this._settings,{}),sunRays:Ir(this._settings,{}),busy:!1}},this._reportedPreflightFingerprint=null,this._openAlignDialog=()=>this._previewAlignDialog(!1),this._toggleOptimizeLivePositions=()=>{const e=this._alignDialog;e&&!e.busy&&e.report.liveMissingPositions.length&&this._previewAlignDialog(!e.removeLiveMissingPositions)},this._optimizeUndoBusy=!1,this._openBackupExport=()=>{this._settingsDialog=null,this._backupExportDialog={kind:"full",planOnly:!1,busy:!1,error:""}},this._openRulesDialog=()=>{if(!this._norm)return;const e=this._settings.icon_rules,t=(e&&e.length?e:Je).map(e=>({...e}));this._rulesDialog={rules:t,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}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}_cssColor(e,t){const i=String(e||"").trim();if(!i)return t;const s=document.createElement("span");s.style.cssText=`position:absolute;visibility:hidden;color:${i}`,this.renderRoot.append(s);const n=getComputedStyle(s).color||t;return s.remove(),n}_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 s=this.renderRoot.querySelector(".editorchrome"),n=this._viewOr(this._baseVb()),o=this.renderRoot.querySelector(".hp-paper"),a=this.renderRoot.querySelector(".hp-backdrop"),r=this.renderRoot.querySelector(".zoomwrap"),l=r?getComputedStyle(r).filter:"",c=Number(/brightness\(([^)]+)\)/.exec(l)?.[1]);return{presentedMode:e,editorChromeHeight:"view"===e?0:s?.getBoundingClientRect().height||0,stageWidth:i.clientWidth,stageHeight:i.clientHeight,viewport:Ig(n,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:a&&Number(getComputedStyle(a).opacity)||1,viewWeight:"view"===e?1:0,editorWeight:"view"===e?0:1,toolbarContentOpacity:"view"===e?0:1}}_viewForModeTarget(e,t,i,s,n){const o=Ni(this._baseVb(),s/Math.max(1,n)),a=Math.min(vv.ZOOM_MAX,Math.max(vv.ZOOM_MIN,e)),r=o.w/a,l=o.h/a,c=t??o.x+o.w/2,h=i??o.y+o.h/2;return this._clampView({x:c-r/2,y:h-l/2,w:r,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 s=this.renderRoot.activeElement;(!s||!s.isConnected||!!s.closest?.(".editorchrome, .stage"))&&this.renderRoot.querySelector(".modetab.active")?.focus?.({preventScroll:!0})})}_cancelModeTransition(e=!0){const t=!!this._modeTransition.state;this._modeTransitionRequest++,this._modeTransitionPreparing=!1,this._modeTransitionForceAtomic=!1,this._modeTransition.cancel(e),e&&t||(this._modeTransitionVisual=null)}_adoptMode(e){this._cancelModeTransition(!1),this._mode=e,"view"!==e&&(this._editorChromeMode=e)}_commitViewModeAtomic(e,t,i,s){if(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._modeTransitionForceAtomic=!1,this._zoom=t,e){const n=this._stageEl?.clientWidth||e.stageWidth,o=Math.max(1,e.stageHeight+e.editorChromeHeight);this._view=this._viewForModeTarget(t,i,s,n,o),this._lastValidStageSize=[n,o]}else this._view=null;this._viewModeSnap=null,this._modeTransitionEditorCamera=null,this._saveZoom(),this.requestUpdate()}_prepareModeTransition(e,t,i,s,n,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 a=this.renderRoot.querySelector(".editorchrome"),r=a?.querySelector(".editorchrome-inner"),l="view"===i?0:r?.scrollHeight||r?.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(s,n,o),void this.requestUpdate();const p=this._viewForModeTarget(s,n,o,d,h),u={presentedMode:i,editorChromeHeight:l,stageWidth:d,stageHeight:h,viewport:Ig(p,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 _=this._modeTransitionForceAtomic;this._modeTransitionForceAtomic=!1,this._modeTransition.start(t,u,i,this._reducedMotion||_?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,s=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?s:"number"==typeof i?"valid"===s.kind||"invalid"===s.kind&&"out-of-range-index"===s.reason?{kind:"pending",value:i}:s:"valid"===s.kind||"invalid"===s.kind&&"unknown-id"!==s.reason?s:{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._clearTransientHover(!0),this._cancelDevicePressFeedback()),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 qf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:"mouse",spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})}_tabPointerDown(e,t){qf({canEdit:this._canEdit,kiosk:this._kiosk,mode:this._mode,pointerType:e.pointerType,spaceCount:this._model.length,fixedFloor:this._hasFixedFloor})&&(fv(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 s=this._model.map(e=>e.id),n=s.indexOf(i);if(n<0)return null;const o=this.renderRoot.querySelectorAll('[data-hp="space-tab"]');for(const a of o){const o=a.dataset.id||"";if(!o||o===i)continue;const r=a.getBoundingClientRect();if(er.right||tr.bottom)continue;const l=s.indexOf(o);return l<0?null:{targetId:o,placement:l=4)))return;var i,s;const n=this._tabDropTargetAt(e.clientX,e.clientY,t.id);t.moved&&t.targetId===n?.targetId&&t.placement===n?.placement||(this._tabDrag={...t,moved:!0,targetId:n?.targetId||null,placement:n?.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 s=this._model.map(e=>e.id),n=function(e,t,i){const s=e.indexOf(t),n=e.indexOf(i);if(s<0||n<0||s===n)return e;const o=e.slice();return o.splice(s,1),o.splice(n,0,t),o}(s,e,t);if(n===s)return;const o=new Map(this._devices.map(e=>[String(e.id),String(e.area||"")])),a=function(e,t,i,s=()=>""){if(!i)return[];const n=[];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 a=("string"==typeof o.area?o.area:"")||s(e)||"";a&&t[a]||n.push({id:e,space:i})}return n}(i.markers||[],Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),s[0]||"",e=>o.get(e)||"");if(a.length){const e=new Map(a.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||[],n),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),s=t.indexOf(e);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(e,i>=0&&s0&&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()}_newContinuityController(){return new Sd(()=>{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(rv,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],s="iso"===e?of(i):i;return{x:s[0],y:s[1]}}_convertProjectionView(e,t){if(e===t)return;const i=this._logicalViewCenter(e);this._view=null;const s=i?"iso"===t?nf([i.x,i.y],0):[i.x,i.y]:null;this._applyView(this._zoom,s?.[0],s?.[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 jg(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=(t=this.ownerDocument,$g.get(t)?.resolved);var t;void 0!==e&&(this._glowScreenBlend=e),this._continuityUnsub?.(),this._continuityUnsub=xd(this.ownerDocument,this._pageVisibility),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),function(e){const t=$g.get(e);if(t)return t.promise;const i={promise:Promise.resolve(!1)};return i.promise=Mg(e).catch(()=>!1).then(e=>(i.resolved=e,e)),$g.set(e,i),i.promise}(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,()=>Rs(this._serverCfg)),this._syncCycleTimer(),window.addEventListener("hashchange",this._onHashChange),this._labsUnsub?.(),this._labsUnsub=function(e,t){Xg.add(t);const i=ef(e);return"undefined"==typeof window&&t(i),()=>{Xg.delete(t)}}(Wf,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},Kf)),!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._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._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._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),this._modeTransition.dispose(),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(),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);for(const e of this._glowFadeTimers.values())clearTimeout(e);for(const e of this._glowEnterRafs.values())cancelAnimationFrame(e);clearTimeout(this._glowFeatherResumeTimer),this._glowFeatherResumeTimer=0,this._glowFeatherSuspendUntil=0,this._glowFadeTimers.clear(),this._glowEnterRafs.clear(),this._glowEnteringSources.clear(),this._glowRenderedSources.clear(),this._glowLastAppearance.clear(),this._glowSourceSeq=0,this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),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)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=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._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=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"]')),s=t.some(e=>e?.classList?.contains?.("editor-secondary")),n=e.ctrlKey||e.metaKey,o=e.key.toLowerCase(),a=/^[a-z]$/.test(o),r="z"===o||!a&&"KeyZ"===e.code,l="y"===o||!a&&"KeyY"===e.code,c=n&&(r&&e.shiftKey||l),h=n&&r&&!e.shiftKey;if("decor"===this._mode){if((h||c)&&i)return;return c?(e.preventDefault(),void this._redoGeometry()):h?(e.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==e.key&&"Backspace"!==e.key||!this._decorSel||i||s?void("Escape"===e.key&&(e.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():"furniture"===this._decorTool?(this._furnPalette=null,this._decorTool="select"):this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(e.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!h&&!c||!i)){if(("Delete"===e.key||"Backspace"===e.key)&&this._physicalSel&&!i&&!s)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 s=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]/cv,e[1]/cv]),segments:this._draftSegmentCms.map((e,t)=>({...s[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 s=e?this._spaceModel()?.room_drafts.find(t=>t.id===e):null;s?(this._activeDraftId=s.id,this._path=s.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,s.segments.map(e=>e.cm),s.id),this._resumeDraftBySpace[this._space]=s.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 getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(e){for(const e of Zf.values())for(const t of e)clearTimeout(t.evict);Zf.clear(),tv=e&&e>0?e:1e4}static _warmBootStats(){let e=0,t=0;const i=[];for(const s of Zf.values())for(const n of s)e++,n.dlg&&(t++,i.push(n.dlg.kind));return{keys:Zf.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(nv)||"{}")||{}}catch{this._zoomBySpace={}}try{const e=JSON.parse(localStorage.getItem(rv)||"{}")||{};this._viewPreference=Object.fromEntries(Object.entries(e).filter(e=>"flat"===e[1]||"iso"===e[1]))}catch{this._viewPreference={}}this._labs=ef(Wf);try{const e=JSON.parse(localStorage.getItem(av)||"null");this._kioskScale={icon:Es(e?.icon),font:Es(e?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(sv)||"null");e&&e.config&&Array.isArray(e.config.spaces)&&(this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._cfgContentFingerprint=e.config_fingerprint||Md(e.config),this._layout=e.layout||{},this._layoutRev=e.layout_rev||0,this._layoutContentFingerprint=e.layout_fingerprint||Md(this._layout),this._virtualLights=mn(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=ev(this._config);if(this._warmKey===e&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const t=this.parentNode,i=this._warmIdx(t),s=Zf.get(e);if(!s||!s.length)return;const n=s.find(e=>e.owner===this._warmGen);if(n)return this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=bd,clearTimeout(n.evict),n.evict=0,n.freed=0,n.live=!0,this._warmSlot=n,this._warmKey=e,n.frameFingerprint&&this._continuity.adoptCompleteFrame(n.frameFingerprint),void(!this._devices.length&&n.devices?.length&&(this._devices=[...n.devices]));const{slot:o,sure:a}=((e,t,i,s)=>{const n=e=>{const t=!!i&&e.place?.deref()===i;return t&&e.idx===s?4:e.live?0:t?3:2};let o=null,a=0,r=0,l=null;for(const i of e){if(i.owner===t)continue;l=i;const e=n(i);e<=0||(e>a?(o=i,a=e,r=1):e===a&&r++)}return!o||r>1?{slot:o||l,sure:!1}:{slot:o,sure:!0}})(s,this._warmGen,t,i);o&&(this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=bd,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},Kf)),this._warmKey=e,a?(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},s.push(this._warmSlot),this._warmTrim(s)))}_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,s=e.owner;e.evict=window.setTimeout(()=>{if(e.evict=0,e.freed!==i||e.owner!==s)return;var n;e.dlg=null,e.vp=(n=e.vp)&&"view"!==n.mode?{...n,mode:"view",zoom:n.snap?.space===n.space?n.snap.zoom:n.zoom,view:null,snap:null,tool:"draw",decorTool:"select",showHidden:!1,selId:null,rszSel:null,decorSel:null}:n,e.frameFingerprint="";const o=Zf.get(t);if(!e.live&&o&&o.length>1){const t=o.indexOf(e);t>=0&&o.splice(t,1)}},tv+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,this._adoptMode("view"!==t.mode&&this._canEdit&&!e.kiosk?t.mode:"view"),this._pendingNavMode="view"===t.mode||this._canEdit||e.kiosk?null:t.mode,this._zoom=t.zoom;const s=this._effectiveProjection(),n=s===t.projection&&this._labsIso===t.activeLabsIso;if(this._view=n&&t.view?{...t.view}:null,this._viewModeSnap=n&&t.snap?{...t.snap}:null,!n&&t.logicalCenter){const e="iso"===s?nf([t.logicalCenter.x,t.logicalCenter.y],0):[t.logicalCenter.x,t.logicalCenter.y];this._applyView(t.zoom,e[0],e[1])}var o;this._tool="opening"===(o=function(e){return"partition"===e?"draw":e}(o=t.tool))?"draw":"string"==typeof o&&pv.has(o)?o:"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}_warmPatch(e,t=!1){if(this._config?.kiosk)return;const i=ev(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 s=Zf.get(i)||[];for(s.push(this._warmSlot),Zf.set(i,s),this._warmTrim(s);Zf.size>8;){const e=Zf.keys().next().value;if(void 0===e||e===i)break;for(const t of Zf.get(e)||[])clearTimeout(t.evict);Zf.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(){this._warmRevivePending=!1;const e=this._warmSlot;if(this._warmReviveTimer=void 0,!e||!e.dlg)return;const t=e.dlg,i=e.freed;if(e.dlg=null,e.freed=0,clearTimeout(e.evict),e.evict=0,i&&!(Date.now()-i>tv)&&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()}}_cacheSnapshot(){if(this._serverCfg)try{this._cfgContentFingerprint=Md(this._serverCfg),this._layoutContentFingerprint=Md(this._layout),this._virtualLights=function(e,t,i){if(e.configRev===i)return e;const s=new Set((Array.isArray(t?.markers)?t.markers:[]).filter(yn).map(e=>e.id));return{...e,configRev:i,off:new Set([...e.off].filter(e=>s.has(e)))}}(this._virtualLights,this._serverCfg,this._cfgRev),localStorage.setItem(sv,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:fn(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,s]of i)if(s&&t.has(s))return{id:s,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=Th(e),s=this._fixedFloorState(i,!0);if(this._hasFixedFloor&&"valid"!==s.kind)return"";const n=this._initialSpaceSelection(i,!0).id||(i.some(e=>e.id===t)?t:i[0]?.id);return i.find(e=>e.id===n)?.bg?.href||""}_visualFrameFingerprint(){const e=this._stageEl,t=e?[e.clientWidth,e.clientHeight]:[0,0];return Cd([this._cfgRev,this._cfgContentFingerprint||Md(this._serverCfg),this._layoutRev,this._layoutContentFingerprint||Md(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 G;const e="connection"===this._continuity.recoveryReason;return W`
e.stopPropagation()} @click=${e=>e.stopPropagation()} @@ -4974,8 +4974,8 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${this._t(e?"continuity.restore_connection":"continuity.restore_plan")} ${"recovery-error"===this._continuity.state?W``:G} -
`}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],s=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]:"")+","+(s?s[0]+"/"+s[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 Th(e).map((t,i)=>{const s=e.spaces[i]?.plan_url;return t.bg&&s?{...t,bg:{...t.bg,href:s}}:t})}_spaceModel(){return this._hasFixedFloor?Td(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Td(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!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._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),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._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=Nn(t,i),n=On(t,i),{areaToSpace:o,markers:a,settings:r,excluded:l,iconRules:c}=e,h=!1!==r.group_lights,d=wo(a),p=yo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=qn(s),m=Wn(a,n),g=new Set(a.map(e=>e.binding)),f=[];for(const e of Object.values(s.devices)){const n=e.area_id;if(!n||!o[n])continue;if("service"===e.entry_type)continue;if(g.has("device:"+e.id))continue;if("active"!==Bn(t,"device:"+e.id,i).kind)continue;const a=jn(s,e.id,_[e.id]||[],m);if(a.partial&&!a.entityIds.length)continue;const r=a.entityIds,d=Un(s,e,r);let p=l.has(d)||"Group"===e.model||/scene/i.test(e.model||"")||/bridge/i.test((e.model||"")+(e.name||""))||"myheat"===d&&!!e.via_device_id;if(!p&&h&&u.has(n)){const t=(e.name_by_user||e.name||"").trim();"mdi:lightbulb"===bo(s,t,e.model,r,c)&&(p=!0)}p&&f.push("device:"+e.id)}return f}({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 s of t){const t="h"+s.slice(s.indexOf(":")+1);e.markers.push({id:t,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...e.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(e=>!i.includes(e))),e.settings=s,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=Xe(e)),this._rulesCompiled}get _fillColors(){return ps(this._settings)}get _excluded(){const e=this._settings.exclude_integrations;return e?new Set(e):Ye}willUpdate(e){e.has("_serverCfg")&&this._cfgEpoch++,this._syncEmptySpaceState(),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._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,s=Math.min(Math.max(t.getBoundingClientRect().top,0),120),n=Math.round(i+s);n>=0&&n!==this._hdrH&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:n,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,s=t.level??1e9;return i!==s?i-s:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptStructuralResponses(e,t,i){const s=e?.config,n=s&&Array.isArray(s.spaces)?s:null,o=Md(n),a=o!==(this._cfgContentFingerprint||Md(this._serverCfg));if(a&&(this._geometryHistory.clear(),this._pendingPhysicalWrites.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=n,this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=gn(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);vn(t)!==vn(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let r=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},s=Md(e);r=s!==(this._layoutContentFingerprint||Md(this._layout)),r&&(this._layout=e,this._layoutContentFingerprint=s),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._haIntegrationVersion="string"==typeof e?.integration_version?e.integration_version:this._haIntegrationVersion,this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),a&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),r&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:a,layoutChanged:r}}_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,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),n=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Md(n)!==(this._cfgContentFingerprint||Md(this._serverCfg))||Md(s?.layout??{})!==(this._layoutContentFingerprint||Md(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(n)))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&&!s?.can_optimize_undo),this._haIntegrationVersion="string"==typeof i?.integration_version?i.integration_version:this._haIntegrationVersion,this._adoptStructuralResponses(i,s),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(tv)||"{}")||{}}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=[],s=(s,n,o,a)=>{s()||i.push(async()=>{const i=await e.subscribeEvents(a,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!s()?n(i):i?.()})};if(s(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),s(()=>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{}}),s(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),s(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=wn(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const n=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=n,n.finally(()=>{this._liveSyncAttempt===n&&(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(Md(t)!==(this._cfgContentFingerprint||Md(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._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._planHassMemo=null,this._haRegistryRelease=Rn(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return An(this.hass)}get _planHass(){const e=this._haRegistry,t=Fn(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const s=Nn(this.hass,e),n=On(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:s,full:n},s}_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+N_)>e?1:0)).join("|"),i=vn(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 s=this._planHass,n=new Map,o=new Map,a=new Set(["sun.sun"]);this._vacFit?.source&&a.add(this._vacFit.source);const r=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void a.add(e);const i=e.slice(0,t),s=e.slice(t+1);"device"===i?r.add(s):"entity"===i&&a.add(s)};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 gi(t))a.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=Yi(e[1]);t?.entity&&a.add(t.entity)}t.entity&&a.add(t.entity),o.set(`decor:${e.id}:${t.id}`,es(t.text,t,s,e=>!!s.entities?.[e]&&!!s.states?.[e]&&!ko(s,e,wo(this._markers))))}const h=ho(s,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])n.set(h_(e.id,t),a_(s,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,s),i=t?Wd(s?.states?.[t]?.attributes):null,n=this._vacRt.get(e.id);o.set(`vacuum:${e.id}`,{source:t,telemetry:i,mapId:i?this._vacMapId(e,i,s):null,runtime:n?{trail:n.trail,lastTs:n.lastTs,moving:n.moving,jump:n.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=c_({sourceSequence:this._hassSequence,hass:s,devices:this._devices,presentations:n,positions:(p=this._model.length>0,u=this._devices,_=e=>this._livePos(e),p?new Map(u.map(e=>[e.id,_(e)])):new Map),facts:o,entityIds:a,deviceIds:r,areaIds:l});var p,u,_;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 _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return Bn(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=An(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&&_v("/config/devices/device/"+encodeURIComponent(e.device_id))}}else _v("/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)}_resign(){this._signer.resign(this.hass,Rs(this._serverCfg))}_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,s]of e)i[t]=s;const s=Md(i);s!==(this._layoutContentFingerprint||Md(this._layout))&&(this._layout=i,this._layoutContentFingerprint=s),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()}}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=Fn(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+ra(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const s=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=So({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 n=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+n.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const s of n)i.set(s,Bn(e,s,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=Ln(),i=Date.now();for(const[s,n]of e)"active"===n.kind?t[s]={kind:"active",ts:i}:"ha_disabled"===n.kind&&(t[s]={kind:"ha_disabled",reason:n.reason,ts:i});const s=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));Mn=s;try{localStorage.setItem(Sn,JSON.stringify(s))}catch{}}(i),this._haBindingCacheKey=o}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),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 r=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==s.get(e.id));r&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._drag&&"ha_disabled"===this._devices.find(e=>e.id===this._drag.id)?.bindingStatus?.kind&&(this._drag=null),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(uv(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_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:s,known:n}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),s=e.filter(e=>!i.has(e));return{fresh:s,known:s.length?[...t,...s]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const e=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:n,new_device_ids:e}},this._saveConfig()}}get _newIds(){const e=this._settings.new_device_ids;return new Set(Array.isArray(e)?e:[])}_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 s=_o(this._renderPlanHass,i.entities);null!=s&&t.push(s)}return Ei(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),s=Math.min(...t),n=Math.min(...i);return{x:s,y:n,w:Math.max(...t)-s,h:Math.max(...i)-n}}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 s=t/100*Xh(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const n=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!n.length)continue;const o=this._roomBounds(t),a=.1*Math.min(o.w,o.h),r=o.w-2*a,l=o.h-2*a,c=Math.max(1,Math.round(Math.sqrt(n.length*r/Math.max(l,1)))),h=Math.ceil(n.length/c),d=r/c,p=l/Math.max(h,1),u=n.map((e,t)=>({x:o.x+a+d*(t%c+.5),y:o.y+a+p*(Math.floor(t/c)+.5)}));Oi(u,o,s,.5*a),n.forEach((t,i)=>e[t.id]=Nh(u[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._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*lv,y:i.y*lv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Nh(Jh(t)):{x:500,y:500}}_savePos(e,t,i){if(this._spaceModelById(e.space)){if(this._norm){const s=this._gridPitch,n=Math.round(t/s)*s,o=Math.round(i/s)*s,a=this._layout[e.id]?.k;this._layout={...this._layout,[e.id]:{s:e.space,x:Hh(n/lv),y:Hh(o/lv),...a?{k:a}:{}}}}else this._layout={...this._layout,[e.id]:{x:Math.round(t),y:Math.round(i)}};this._dirtyPos.add(e.id),this._persistLayout()}}_visualSamples(e){return t_(this._renderPlanHass,e,this._renderDevices,void 0,this._fullRegistryHass).samples}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const s=i?null:this._renderDeviceSnapshot?.presentations.get(h_(e.id,t));return s||a_(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,o_({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=go(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:mo(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 Bs(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_liveHum(e){return this._config?.show_temperature&&e.primary&&fo(this.hass,e.primary)?vo(this.hass,e.entities):null}_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,uv(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}_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(e.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const s=vp(i.tapAction,i.primary?.split(".")[0]),n=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===s){const e=this._toggleIntent(i);if(!e)return;if(!$p(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=$p(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=wn(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:s}=t;this._startDevicePressFeedback(i.id),this.hass.callService(s.domain,s.service,s.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},s=((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 n=this._toggleConfirmationLines(e);if(!n.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:s}),lines:n,initialIntent:e,deviceId:i.id,exec:()=>{const s=this._devices.find(e=>e.id===i.id),n=s?this._toggleIntent(s):null;n&&!$p(n)&&this._showUnavailableToggleTargets(n)||(n&&Mp(e,n)?t(n):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===s)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===s){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),s=this.hass.states[e];if(!t||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||e;return void n(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"===s&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||$p(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Sp.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Sp.has(s.reason))continue;const e=String(s.name||s.entityId||s.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 s=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),n=Number.isFinite(s)?Math.max(.95,Math.min(1,s)):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(n)},{scale:"0.95",offset:.5},{scale:"1"}],a=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,a);const r=()=>{this._devicePressAnimations.get(e)===a&&this._devicePressAnimations.delete(e)};a.addEventListener("finish",r,{once:!0}),a.addEventListener("cancel",r,{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 la(ra(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){const t=`${e}.aria`,i=ra(this.hass,this._config?.language);return ca(i,e)&&ca(i,t)?W``:G}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 s=this._pos(i);t.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,s=Math.cos(i)*e.rlen/2,n=Math.sin(i)*e.rlen/2,o=qh([[e.rx-s,e.ry-n],[e.rx+s,e.ry+n]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){const s=qh("line"===e.kind?[[e.x1*lv,e.y1*i],[e.x2*lv,e.y2*i]]:"text"===e.kind?[[e.x*lv,e.y*i]]:jr({x:e.x*lv,y:e.y*i,w:e.w*lv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=qh(i);e&&t.push(e)}}return jh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:lv,h:lv};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 s=Kh(this._contentItems(e));let n=s.all||Yh(e),o=this._showFar?n:s.core||Yh(e);return t&&t.id===e.id&&i&&t.grow&&(o=hv(t.rect,o),n=hv(t.all,n)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,outliers:s.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),s=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})),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=`${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=uf(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=uf(t,-1)}else t=uf(t,e?1:0);else for(const i of e)t=uf(t,i.codePointAt(0)||0);else t=uf(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:s,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:ef,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?Ec(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,lv,o):null;if(r&&("failed-core"===r.status||"not-applicable"===r.status))throw new Error("wall boolean geometry failed");const l=r?.paperGeom??zc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,lv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||Bc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,lv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?zp(i.partitionHost,s):t.length||"gate"===e.type?Uc(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:s}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(_f([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 s;if("passage"===e.type)s=[];else if("gate"===e.type){const n=10*e.face.side;s=[gf(e,0,[-i,0],[i,0],n,0,.88*t),gf(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[gf(e,0,[-i,0],[i,0],-90,.27*t,.78*t),gf(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[gf(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(s.map(e=>Object.freeze(e)))}}({...e,face:o},n)});return{walls:r?.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,s=e.build(),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=function(e){let t=1/0,i=1/0,s=-1/0,n=-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),s=Math.max(s,e.hinge[0]+o),n=Math.max(n,e.hinge[1]+o)}return _f([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=af({rect:a?hv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=ef,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const s=[],n=[],o=[];let a=0;for(let r=0;r<(e||[]).length;r++){const l=e[r];for(let e=0;e<(l||[]).length;e++){const c=cf(l[e],e>0);if(!(c.length<3||Math.abs(lf(c))<1e-9)){s.push(hf(c,t,i)),a+=c.length;for(let s=0;se.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:s.join(" "),sides:n,contactPath:o.join(" "),edgeCount:a}}(s.walls,ef,n),c={geometry:l,floor:pf(s.floor,o),openings:s.openings,frame:r};return Jf(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,s=this._isoFallback,"iso"===t&&i&&!s.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,s}_scenePoint(e){return"iso"===this._renderProjection?sf(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=nf([e.x,e.y]),i=nf([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=af({rect:this._frameOf().rect,wallHeight:wh(64,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._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
+
`}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],s=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]:"")+","+(s?s[0]+"/"+s[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 Th(e).map((t,i)=>{const s=e.spaces[i]?.plan_url;return t.bg&&s?{...t,bg:{...t.bg,href:s}}:t})}_spaceModel(){return this._hasFixedFloor?Td(this._model,this._space):(e=this._model,t=this._space,e.find(e=>e.id===t)??e[0]);var e,t}_spaceModelById(e){return Td(this._model,e)}_syncEmptySpaceState(){if(!!this._serverCfg&&0===this._serverCfg.spaces.length){if(!this._emptySpaceStateActive){this._emptySpaceStateActive=!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._rlResize=null,this._vacFit=null,this._compassDrag=!1,this._cancelModeTransition(!1),this._mode="view",this._clearGeometryGesture(),this._geometryHistory.clear(),this._resumeDraftBySpace={},this._tip=null,this._hoverRoom=null,this._openingInfo=null,this._closeInfoCard(),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._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const e=this._serverCfg,t=function(e){const t=e.hass,i=e.registry||An(t),s=Nn(t,i),n=On(t,i),{areaToSpace:o,markers:a,settings:r,excluded:l,iconRules:c}=e,h=!1!==r.group_lights,d=wo(a),p=yo(s,h).filter(e=>!ko(s,e.eid,d)),u=new Set(p.map(e=>e.area)),_=qn(s),m=Wn(a,n),g=new Set(a.map(e=>e.binding)),f=[];for(const e of Object.values(s.devices)){const n=e.area_id;if(!n||!o[n])continue;if("service"===e.entry_type)continue;if(g.has("device:"+e.id))continue;if("active"!==Bn(t,"device:"+e.id,i).kind)continue;const a=jn(s,e.id,_[e.id]||[],m);if(a.partial&&!a.entityIds.length)continue;const r=a.entityIds,d=Un(s,e,r);let p=l.has(d)||"Group"===e.model||/scene/i.test(e.model||"")||/bridge/i.test((e.model||"")+(e.name||""))||"myheat"===d&&!!e.via_device_id;if(!p&&h&&u.has(n)){const t=(e.name_by_user||e.name||"").trim();"mdi:lightbulb"===bo(s,t,e.model,r,c)&&(p=!0)}p&&f.push("device:"+e.id)}return f}({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 s of t){const t="h"+s.slice(s.indexOf(":")+1);e.markers.push({id:t,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...e.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(e=>!i.includes(e))),e.settings=s,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=Xe(e)),this._rulesCompiled}get _fillColors(){return ps(this._settings)}get _excluded(){const e=this._settings.exclude_integrations;return e?new Set(e):Ye}willUpdate(e){e.has("_serverCfg")&&this._cfgEpoch++,this._syncEmptySpaceState(),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._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,s=Math.min(Math.max(t.getBoundingClientRect().top,0),120),n=Math.round(i+s);n>=0&&n!==this._hdrH&&(this._hdrH=n),n>=0&&!this._booting&&!this._config?.kiosk&&e.clientHeight>0&&this._warmPatch({hdrH:n,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,s=t.level??1e9;return i!==s?i-s:e.name.localeCompare(t.name)}),i}(this.hass);e.length?this._importDialog={floors:e.map(e=>({...e,checked:!0}))}:this._openSpaceDialog("create")}}_adoptStructuralResponses(e,t,i){const s=e?.config,n=s&&Array.isArray(s.spaces)?s:null,o=Md(n),a=o!==(this._cfgContentFingerprint||Md(this._serverCfg));if(a&&(this._geometryHistory.clear(),this._pendingPhysicalWrites.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=n,this._cfgContentFingerprint=o),this._cfgRev=e?.rev??this._cfgRev,e&&("virtual_lights"in e||"config"in e)){const t=gn(this._virtualLights,e.virtual_lights,this._cfgRev,"virtual_lights"in e);vn(t)!==vn(this._virtualLights)&&(this._virtualLights=t,this._capturedSnapshotVirtual="")}let r=!1;if(void 0!==t||void 0!==i){const e=i??t?.layout??{},s=Md(e);r=s!==(this._layoutContentFingerprint||Md(this._layout)),r&&(this._layout=e,this._layoutContentFingerprint=s),this._layoutRev=t?.rev??this._layoutRev}return this._canOptimizeUndo=!(!e?.can_optimize_undo&&!t?.can_optimize_undo),this._haIntegrationVersion="string"==typeof e?.integration_version?e.integration_version:this._haIntegrationVersion,this._undoKind=e?.undo_kind||t?.undo_kind||null,"boolean"==typeof e?.can_write&&(this._serverCanWrite=e.can_write),a&&this._continuity.note("config-candidate",{configRev:this._cfgRev}),r&&this._continuity.note("layout-candidate",{layoutRev:this._layoutRev}),{configChanged:a,layoutChanged:r}}_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,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]),n=i?.config&&Array.isArray(i.config.spaces)?i.config:null,o=Md(n)!==(this._cfgContentFingerprint||Md(this._serverCfg))||Md(s?.layout??{})!==(this._layoutContentFingerprint||Md(this._layout));if(o){if(!await this._signer.prepareImage(this.hass,this._candidateBackdrop(n)))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&&!s?.can_optimize_undo),this._haIntegrationVersion="string"==typeof i?.integration_version?i.integration_version:this._haIntegrationVersion,this._adoptStructuralResponses(i,s),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(iv)||"{}")||{}}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=[],s=(s,n,o,a)=>{s()||i.push(async()=>{const i=await e.subscribeEvents(a,o);t===this._liveSyncGeneration&&this.isConnected&&this.hass?.connection===e&&!s()?n(i):i?.()})};if(s(()=>this._unsubCfg,e=>{this._unsubCfg=e},"houseplan_config_updated",e=>{const t=Number(e?.data?.rev??-1);t!==this._cfgRev&&this._reloadConfigOnly(!1,t)}),s(()=>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{}}),s(()=>this._unsubLayout,e=>{this._unsubLayout=e},"houseplan_layout_updated",e=>this._onLayoutEvent(Number(e?.data?.rev??-1))),s(()=>this._unsubVirtual,e=>{this._unsubVirtual=e},"houseplan_virtual_light_updated",e=>{const t=wn(this._virtualLights,e?.data);t!==this._virtualLights&&(this._virtualLights=t,this._capturedSnapshotVirtual="",this._cacheSnapshot(),this.requestUpdate())}),!i.length)return;const n=function(e){return Promise.allSettled(e.map(e=>Promise.resolve().then(e)))}(i).then(()=>{});this._liveSyncAttempt=n,n.finally(()=>{this._liveSyncAttempt===n&&(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(Md(t)!==(this._cfgContentFingerprint||Md(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._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._planHassMemo=null,this._haRegistryRelease=Rn(this.hass,this._onHaRegistryUpdate),this._onHaRegistryUpdate())}get _haRegistry(){return An(this.hass)}get _planHass(){const e=this._haRegistry,t=Fn(this.hass,e),i=this._planHassMemo;if(i&&i.hass===this.hass&&i.sig===t)return i.active;const s=Nn(this.hass,e),n=On(this.hass,e);return this._planHassMemo={hass:this.hass,sig:t,active:s,full:n},s}_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+O_)>e?1:0)).join("|"),i=vn(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 s=this._planHass,n=new Map,o=new Map,a=new Set(["sun.sun"]);this._vacFit?.source&&a.add(this._vacFit.source);const r=new Set,l=new Set,c=e=>{if(!e)return;const t=e.indexOf(":");if(t<0)return void a.add(e);const i=e.slice(0,t),s=e.slice(t+1);"device"===i?r.add(s):"entity"===i&&a.add(s)};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 gi(t))a.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=Yi(e[1]);t?.entity&&a.add(t.entity)}t.entity&&a.add(t.entity),o.set(`decor:${e.id}:${t.id}`,es(t.text,t,s,e=>!!s.entities?.[e]&&!!s.states?.[e]&&!ko(s,e,wo(this._markers))))}const h=ho(s,this._devices,null,this._virtualLights);for(const e of this._devices){for(const t of[!1,!0])n.set(d_(e.id,t),r_(s,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,s),i=t?Wd(s?.states?.[t]?.attributes):null,n=this._vacRt.get(e.id);o.set(`vacuum:${e.id}`,{source:t,telemetry:i,mapId:i?this._vacMapId(e,i,s):null,runtime:n?{trail:n.trail,lastTs:n.lastTs,moving:n.moving,jump:n.jump}:null,server:this._vacSrvTrails[e.id]||null})}}const d=h_({sourceSequence:this._hassSequence,hass:s,devices:this._devices,presentations:n,positions:(p=this._model.length>0,u=this._devices,_=e=>this._livePos(e),p?new Map(u.map(e=>[e.id,_(e)])):new Map),facts:o,entityIds:a,deviceIds:r,areaIds:l});var p,u,_;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 _fullRegistryHass(){return this._planHass,this._planHassMemo?.full||this.hass}_bindingStatus(e){return Bn(this.hass,e,this._haRegistry)}houseplanDiagnostics(){const e=function(e){const t=An(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&&mv("/config/devices/device/"+encodeURIComponent(e.device_id))}}else mv("/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)}_resign(){this._signer.resign(this.hass,Rs(this._serverCfg))}_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,s]of e)i[t]=s;const s=Md(i);s!==(this._layoutContentFingerprint||Md(this._layout))&&(this._layout=i,this._layoutContentFingerprint=s),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()}}_maybeRebuildDevices(){const e=this.hass;if(!e?.devices||!e?.entities||!e?.areas)return;const t=this._haRegistry,i=Fn(e,t)+":"+Object.keys(e.areas).length+":"+(this._norm?"n":"l")+":"+ra(e,this._config?.language);if(i===this._regSignature&&this._devices.length)return;this._regSignature=i;const s=new Map(this._devices.map(e=>[e.id,e.bindingStatus?.kind||"active"]));this._devices=So({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 n=this._markers.filter(e=>!e.removed&&"virtual"!==e.binding).map(e=>e.binding).sort(),o=t.revision+":"+n.join("|");if(t.authoritative&&o!==this._haBindingCacheKey){const i=new Map;for(const s of n)i.set(s,Bn(e,s,t));!function(e){if("undefined"==typeof localStorage||!e.size)return;const t=Hn(),i=Date.now();for(const[s,n]of e)"active"===n.kind?t[s]={kind:"active",ts:i}:"ha_disabled"===n.kind&&(t[s]={kind:"ha_disabled",reason:n.reason,ts:i});const s=Object.fromEntries(Object.entries(t).sort((e,t)=>t[1].ts-e[1].ts).slice(0,1500));Mn=s;try{localStorage.setItem(Sn,JSON.stringify(s))}catch{}}(i),this._haBindingCacheKey=o}this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),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 r=this._devices.some(e=>"ha_disabled"===e.bindingStatus?.kind&&"ha_disabled"!==s.get(e.id));r&&("ha_disabled"!==this._infoCard?.bindingStatus?.kind&&"ha_disabled"!==this._devices.find(e=>e.id===this._infoCard?.id)?.bindingStatus?.kind||this._closeInfoCard(),this._drag&&"ha_disabled"===this._devices.find(e=>e.id===this._drag.id)?.bindingStatus?.kind&&(this._drag=null),clearTimeout(this._holdTimer),this._holdFired=!1,this._tip=null,this._tapConfirm=null),this._nativeMoreInfoEntity&&!this._planEntityAvailable(this._nativeMoreInfoEntity)&&(_v(this,"hass-more-info",{entityId:null}),this._nativeMoreInfoEntity=null)}_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:s,known:n}=function(e,t){if(!Array.isArray(t))return{fresh:[],known:[...e]};const i=new Set(t),s=e.filter(e=>!i.has(e));return{fresh:s,known:s.length?[...t,...s]:t}}(e,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const e=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:n,new_device_ids:e}},this._saveConfig()}}get _newIds(){const e=this._settings.new_device_ids;return new Set(Array.isArray(e)?e:[])}_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 s=_o(this._renderPlanHass,i.entities);null!=s&&t.push(s)}return Ei(t)}_roomBounds(e){if(e.poly&&e.poly.length){const t=e.poly.map(e=>e[0]),i=e.poly.map(e=>e[1]),s=Math.min(...t),n=Math.min(...i);return{x:s,y:n,w:Math.max(...t)-s,h:Math.max(...i)-n}}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 s=t/100*Xh(i)*1.3;for(const t of i.rooms){if(!t.area)continue;const n=this._devices.filter(e=>e.area===t.area&&e.space===i.id);if(!n.length)continue;const o=this._roomBounds(t),a=.1*Math.min(o.w,o.h),r=o.w-2*a,l=o.h-2*a,c=Math.max(1,Math.round(Math.sqrt(n.length*r/Math.max(l,1)))),h=Math.ceil(n.length/c),d=r/c,p=l/Math.max(h,1),u=n.map((e,t)=>({x:o.x+a+d*(t%c+.5),y:o.y+a+p*(Math.floor(t/c)+.5)}));Oi(u,o,s,.5*a),n.forEach((t,i)=>e[t.id]=Nh(u[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._layout[e.id];if(i)if(this._norm){if(i.s===e.space)return{x:i.x*cv,y:i.y*cv}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[e.id]?this._defPos[e.id]:t?Nh(Jh(t)):{x:500,y:500}}_savePos(e,t,i){if(this._spaceModelById(e.space)){if(this._norm){const s=this._gridPitch,n=Math.round(t/s)*s,o=Math.round(i/s)*s,a=this._layout[e.id]?.k;this._layout={...this._layout,[e.id]:{s:e.space,x:Lh(n/cv),y:Lh(o/cv),...a?{k:a}:{}}}}else this._layout={...this._layout,[e.id]:{x:Math.round(t),y:Math.round(i)}};this._dirtyPos.add(e.id),this._persistLayout()}}_visualSamples(e){return i_(this._renderPlanHass,e,this._renderDevices,void 0,this._fullRegistryHass).samples}_devicePresentation(e,t=!1!==this._config?.show_signal,i=!1){const s=i?null:this._renderDeviceSnapshot?.presentations.get(d_(e.id,t));return s||r_(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,a_({...i,visual:t}).join(" "))}_liveTemp(e){if(!this._config?.show_temperature)return null;if(!0===e.marker?.use_climate_temp){const t=go(this._renderPlanHass,e.entities);if(null!=t)return t}return"mdi:thermometer"!==e.icon&&"mdi:air-filter"!==e.icon?null:mo(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 Bs(e.split(".")[0],t?.attributes?.device_class||i?.device_class||i?.original_device_class)})}_liveHum(e){return this._config?.show_temperature&&e.primary&&fo(this.hass,e.primary)?vo(this.hass,e.entities):null}_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,_v(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}_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(e.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(t);const i=this._devices.find(e=>e.id===t.id);if(!i)return;const s=vp(i.tapAction,i.primary?.split(".")[0]),n=(e,t)=>{i.marker?.tap_confirm?this._tapConfirm={kind:"run",text:e,exec:t}:t()};if("toggle"===s){const e=this._toggleIntent(i);if(!e)return;if(!$p(e))return void this._showUnavailableToggleTargets(e);const t=e=>{const t=$p(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=wn(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:s}=t;this._startDevicePressFeedback(i.id),this.hass.callService(s.domain,s.service,s.data).catch(e=>this._showToast(this._t("toast.error",{err:this._errText(e)})))},s=((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 n=this._toggleConfirmationLines(e);if(!n.length)return;this._tapConfirm={kind:"toggle",text:this._t("confirm.tap_toggle",{name:s}),lines:n,initialIntent:e,deviceId:i.id,exec:()=>{const s=this._devices.find(e=>e.id===i.id),n=s?this._toggleIntent(s):null;n&&!$p(n)&&this._showUnavailableToggleTargets(n)||(n&&Mp(e,n)?t(n):this._showToast(this._t("toast.tap_target_changed")))}}}else t(e);return}var o;if("info"===s)return this._interruptViewGesture(),void(this._infoCard=i);if(this._deviceBindingActive(i)){if("run"===s){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),s=this.hass.states[e];if(!t||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||e;return void n(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"===s&&i.primary?this._openMoreInfo(i.primary):this._infoCard=i}}_showUnavailableToggleTargets(e){const t=function(e){if(!e||"group"!==e.kind||$p(e)||"configured-targets-missing"!==e.noneReason)return[];if(!e.skippedTargets.length||e.skippedTargets.some(e=>!Sp.has(e.reason)&&"secure"!==e.reason))return[];const t=[],i=new Set;for(const s of e.skippedTargets){if(!Sp.has(s.reason))continue;const e=String(s.name||s.entityId||s.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 s=Number.parseFloat(this.ownerDocument.defaultView?.getComputedStyle(i).scale||""),n=Number.isFinite(s)?Math.max(.95,Math.min(1,s)):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(n)},{scale:"0.95",offset:.5},{scale:"1"}],a=i.animate(o,{duration:200,easing:"cubic-bezier(.22,.61,.36,1)"});this._devicePressAnimations.set(e,a);const r=()=>{this._devicePressAnimations.get(e)===a&&this._devicePressAnimations.delete(e)};a.addEventListener("finish",r,{once:!0}),a.addEventListener("cancel",r,{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 la(ra(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){const t=`${e}.aria`,i=ra(this.hass,this._config?.language);return ca(i,e)&&ca(i,t)?W``:G}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 s=this._pos(i);t.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(e.id===this._space){for(const e of this._openingsR){const i=Number(e.angle)*Math.PI/180,s=Math.cos(i)*e.rlen/2,n=Math.sin(i)*e.rlen/2,o=qh([[e.rx-s,e.ry-n],[e.rx+s,e.ry+n]]);o&&t.push(o)}const i=this._decorH;for(const e of this._decorList){const s=qh("line"===e.kind?[[e.x1*cv,e.y1*i],[e.x2*cv,e.y2*i]]:"text"===e.kind?[[e.x*cv,e.y*i]]:jr({x:e.x*cv,y:e.y*i,w:e.w*cv,h:e.h*i,angle:e.angle}));s&&t.push(s)}for(const i of this._physicalBodiesR(e)){const e=qh(i);e&&t.push(e)}}return jh(e,t)}_frameOf(){const e=this._spaceModel();if(!e){this._frame=null;const e={x:0,y:0,w:cv,h:cv};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 s=Kh(this._contentItems(e));let n=s.all||Yh(e),o=this._showFar?n:s.core||Yh(e);return t&&t.id===e.id&&i&&t.grow&&(o=dv(t.rect,o),n=dv(t.all,n)),this._frame={id:e.id,model:e,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:o,all:n,outliers:s.outliers},this._frame}_isoSource(){const e=this._spaceModel();if(!e)return null;const t=this._spaceWalls,i=this._openCuts(),s=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})),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=`${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=_f(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=_f(t,-1)}else t=_f(t,e?1:0);else for(const i of e)t=_f(t,i.codePointAt(0)||0);else t=_f(t,e)};return i(e),t.toString(36)}({rooms:e.rooms,walls:t,openCuts:i,openings:s,partitions:e.partitions,roomDrafts:e.room_drafts,columns:e.wall_columns,cellCm:this._cellCm,gridPitch:this._gridPitch,wallKeyPitch:this._wallKeyPitch,camera:tf,wallHeight:n,floorEdgeHeight:o,algorithm:3})}`;return{key:a,build:()=>{const o=rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e)).all,a=this._roomWallOpeningInputs(this._openingsR,e),r=t.length||o.length?Ec(e.rooms,t,i,a,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,o):null;if(r&&("failed-core"===r.status||"not-applicable"===r.status))throw new Error("wall boolean geometry failed");const l=r?.paperGeom??zc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv);if(!l)throw new Error("floor boolean geometry failed");const c=r?.openingIndex||Bc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv),h=s.map(e=>{const i=this._openingsR[e.sourceIndex],s="gate"===e.type?!e.flipV:e.flipV,o=i?.partitionHost?zp(i.partitionHost,s):t.length||"gate"===e.type?Uc(c,{x:e.x,y:e.y,angle:e.angle,length:e.length,flip_v:s}):{ox:0,oy:0,cm:0,side:-1};return function(e,t=64){if(!(mf([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 s;if("passage"===e.type)s=[];else if("gate"===e.type){const n=10*e.face.side;s=[ff(e,0,[-i,0],[i,0],n,0,.88*t),ff(e,1,[i,0],[-i,0],-n,0,.88*t)]}else s="window"===e.type?[ff(e,0,[-i,0],[i,0],-90,.27*t,.78*t),ff(e,1,[i,0],[-i,0],90,.27*t,.78*t)]:[ff(e,0,[-i,0],[e.length,0],-90,0,.92*t)];return{id:e.id,sourceIndex:e.sourceIndex,type:e.type,leaves:Object.freeze(s.map(e=>Object.freeze(e)))}}({...e,face:o},n)});return{walls:r?.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,s=e.build(),n=wh(64,this._cellCm),o=wh(10,this._cellCm),a=function(e){let t=1/0,i=1/0,s=-1/0,n=-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),s=Math.max(s,e.hinge[0]+o),n=Math.max(n,e.hinge[1]+o)}return mf([t,i,s,n])?{x:t,y:i,w:s-t,h:n-i}:null}(s.openings),r=rf({rect:a?dv(i,a):i,wallHeight:n,openingHeight:n,floorDepth:o}),l=function(e,t=tf,i=64){if(!Number.isFinite(i)||i<0)throw new Error("invalid wall height");const s=[],n=[],o=[];let a=0;for(let r=0;r<(e||[]).length;r++){const l=e[r];for(let e=0;e<(l||[]).length;e++){const c=hf(l[e],e>0);if(!(c.length<3||Math.abs(cf(c))<1e-9)){s.push(df(c,t,i)),a+=c.length;for(let s=0;se.depth-t.depth||e.polygon-t.polygon||e.ring-t.ring||e.edge-t.edge),{topPath:s.join(" "),sides:n,contactPath:o.join(" "),edgeCount:a}}(s.walls,tf,n),c={geometry:l,floor:uf(s.floor,o),openings:s.openings,frame:r};return Xf(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,s=this._isoFallback,"iso"===t&&i&&!s.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,s}_scenePoint(e){return"iso"===this._renderProjection?nf(e,0):e}_floorView(e){if("iso"!==this._renderProjection)return e;const t=of([e.x,e.y]),i=of([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=rf({rect:this._frameOf().rect,wallHeight:wh(64,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._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -4983,7 +4983,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 style="left:${r.toFixed(1)}%;top:${l.toFixed(1)}%" @click=${e=>{e.stopPropagation(),this._fitAll()}}> - `}_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:Ni(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 s=t.clientHeight+(i?.getBoundingClientRect().height||0);return s<=0?e.w:this._viewForModeTarget(this._zoom,void 0,void 0,t.clientWidth,s).w}_screenToVb(e,t){const i=this._stageEl,s=this._viewOr(this._baseVb()),n=i?.clientWidth||1,o=i?.clientHeight||1;return[s.x+e/n*s.w,s.y+t/o*s.h]}_clampView(e,t){const i=(e,t,i,s)=>{const n=1*Math.max(t,s),o=i-n,a=i+s-t+n;return Math.max(Math.min(o,a),Math.min(Math.max(o,a),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){const s=this._baseVb(),n=Ni(s,this._stageAspect()),o=Math.min(fv.ZOOM_MAX,Math.max(fv.ZOOM_MIN,e)),a=n.w/o,r=n.h/o,l=this._viewOr(s),c=t??l.x+l.w/2,h=i??l.y+l.h/2,d=this._clampView({x:c-a/2,y:h-r/2,w:a,h:r},n);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 s=t-this._bootStart;s>=1200||s>=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},Vf)}_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)return;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,s=!!i&&Math.abs(i[0]-t[0])<=.5&&Math.abs(i[1]-t[1])<=.5,n=this._viewportInvalidAt?Date.now()-this._viewportInvalidAt:0;if(this._viewportInvalidAt=0,!i)return this._lastValidStageSize=t,void(this._view||this._applyView(this._zoom));s?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 s=this._view;n>=bd?this._beginContinuityCandidate("stage-size-restored",!0,"stage-size"):this._continuity.hasCompleteFrame&&this._beginContinuityCandidate("stage-resize",!0,"stage-size"),this._applyView(this._zoom,s?s.x+s.w/2:void 0,s?s.y+s.h/2:void 0)})))}_zoomAt(e,t,i){const s=this._stageEl;if(!s)return;const n=Ni(this._baseVb(),this._stageAspect()),o=Math.min(fv.ZOOM_MAX,Math.max(fv.ZOOM_MIN,i));"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1));const a=s.clientWidth,r=s.clientHeight,l=this._screenToVb(e,t),c=n.w/o,h=n.h/o;this._zoom=o,this._view=this._clampView({x:l[0]-e/a*c,y:l[1]-t/r*h,w:c,h:h},n)}_onWheel(e){const t=this._stageEl;if(!t)return;e.preventDefault();const i=t.getBoundingClientRect(),s=e.deltaY<0?1.15:1/1.15;this._zoomAt(e.clientX-i.left,e.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(e){const t=this._stageEl;t&&(this._zoomAt(t.clientWidth/2,t.clientHeight/2,this._zoom*(e>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const e=this._baseVb();"opening"===this._tool&&(this._cursorPt=null,this._clearOpeningPlacement(!1)),this._zoom=1,this._view=Ni(e,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(sv,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){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){if(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._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)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 t=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:e.clientX,sy:e.clientY,vx:t.x,vy:t.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){"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],s=i[1]-t.a[1],n=Math.max(Math.abs(e),Math.abs(s));i=this._snap([t.a[0]+(e<0?-n:n),t.a[1]+(s<0?-n:n)])}return void(this._decorDraft={...t,b:i})}if(this._decorMove?.pid!==e.pointerId)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){"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(),s=(e[0].x+e[1].x)/2-i.left,n=(e[0].y+e[1].y)/2-i.top;this._zoomAt(s,n,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._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");const s=this._stageEl;if("pan"===this._panLock&&s){const e=this._baseVb(),n=this._viewOr(e),o=Ni(e,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-t/(s.clientWidth||1)*n.w,y:this._panStart.vy-i/(s.clientHeight||1)*n.h,w:n.w,h:n.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){this._markup&&("opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover())}_stagePointerUp(e){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const t=this._swipeStart;if(this._swipeStart=null,t&&t.id===e.pointerId){const i=e.clientX-t.x,s=e.clientY-t.y;if(Math.abs(i)+Math.abs(s)<8){const e=Date.now();e-this._lastTap<350&&this._resetZoom(),this._lastTap=e}const n="pan"===this._panLock?null:function(e,t,i,s,n,o=60){if(i>1.001||s.length<2)return null;if(Math.abs(e)e.id),this._space);n&&this._slideTo(n,i<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);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,setTimeout(()=>this._suppressClick=!1,0)),t&&0===this._pointers.size&&this.requestUpdate()}_clickRoom(e){!this._suppressClick&&e.area&&_v("/config/areas/area/"+e.area)}_pointerDown(e,t){if("plan"===this._mode)return;if("view"===this._mode){this._holdFired=!1,clearTimeout(this._holdTimer);const i=e.pointerId,s=e.currentTarget;return void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._interruptViewGesture(i,s),this._infoCard=t},600))}if("ha_disabled"===t.bindingStatus?.kind)return;e.preventDefault();const i=this._pos(t);this._drag={id:t.id,sx:e.clientX,sy:e.clientY,ox:i.x,oy:i.y,moved:!1},gv(e),this._tip=null}_pointerMove(e,t){if(!this._drag||this._drag.id!==t.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),n=i.getBoundingClientRect(),o=this._viewOr(s),a=(e.clientX-this._drag.sx)/n.width*o.w,r=(e.clientY-this._drag.sy)/n.height*o.h;Math.abs(e.clientX-this._drag.sx)+Math.abs(e.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=Oh(this._drag.ox+a),c=Oh(this._drag.oy+r);this._savePos(t,l,c)}_pointerUp(e,t){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==t.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=t.id,window.setTimeout(()=>this._drag=null,0))}_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(rv))t.toggleAttribute("data-pointer-hover",e)}_syncPointerHoverSubtree(e){if(e.nodeType!==Node.ELEMENT_NODE)return;const t=e,i=this._pointerModality.hoverEnabled;t.matches(rv)&&t.toggleAttribute("data-pointer-hover",i);for(const e of t.querySelectorAll(rv))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),"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,s=Math.hypot(t.x-i.x,t.y-i.y)/(this._pinchStart.dist||1),n=this._stageEl.getBoundingClientRect();this._zoomAt((t.x+i.x)/2-n.left,(t.y+i.y)/2-n.top,this._pinchStart.zoom*s),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._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,s,n,o){this._notePointer(e),this._pointerModality.hoverEnabled&&(this._drag||(this._tip={x:e.clientX,y:e.clientY,title:t,meta:i,lqi:s,temp:n,hum:o}))}get _gridPitch(){return zh}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,s){return Math.hypot(t[0]-e[0],t[1]-e[1])/i*s}(e,t,this._gridPitch,this._cellCm);return li(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,lv}get _segments(){const e=this._curSpaceCfg,t=this._spaceH;return pi(e?.rooms||[]).map(e=>[e[0]*lv,e[1]*t,e[2]*lv,e[3]*t])}_savedNav(){if(this._hasFixedFloor)return null;try{return JSON.parse(localStorage.getItem(nv)||"null")}catch{return null}}_saveNav(){if(!this._hasFixedFloor)try{localStorage.setItem(nv,JSON.stringify({space:this._space}))}catch{}}_leaveCardRoute(){if(this._routeDepartureHandled)return;this._routeDepartureHandled=!0,"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._settingsDialog=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){this._endTabDrag(),this._pendingNavMode=null;const i=this._spaceModel();if(!i)return this._cancelModeTransition(!1),void(this._mode="view");if(this._kiosk&&"view"!==e)return;if(this._mode===e){if(!t&&"view"===e&&this._modeTransitionBusy){const t=this._currentModeVisual(e),i=this._modeTransitionTargetZoom,s=this._modeTransitionTargetCenterX,n=this._modeTransitionTargetCenterY;this._cancelModeTransition(!1),this._commitViewModeAtomic(t,i,s,n)}return}if(this._bootSoftCancel(),("plan"===e||"decor"===e)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));if(this._wallFaceBatch&&this._roomDialogCancel(),"plan"===this._mode&&"draw"===this._tool&&!this._finishWallChain())return;this._clearTransientHover(!0),this._cancelDevicePressFeedback();const s=this._mode,n=this._effectiveProjection(),o=this._currentModeVisual(s),a=this._modeTransitionBusy,r=o&&"view"!==s&&"view"!==e?{...o,toolbarContentOpacity:this._reducedMotion?1:.35}:o;this._cancelModeTransition(!1),this._editorSecondary.closeForNavigation(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const l=!i.bg&&"view"===e!=("view"===s),c=a&&"view"===s&&"view"!==e?this._modeTransitionEditorCamera:null;let h=c?.zoom??(a?this._modeTransitionTargetZoom:this._zoom),d=c?c.centerX:a?this._modeTransitionTargetCenterX:r?.viewport.centerX,p=c?c.centerY:a?this._modeTransitionTargetCenterY:r?.viewport.centerY;if("view"===s&&"view"!==e&&!a){const t=this._view;if(this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?this._logicalViewCenter(n)?.x:void 0,cy:t?this._logicalViewCenter(n)?.y:void 0},"iso"===n){const t=this._logicalViewCenter("iso");d=t?.x,p=t?.y,this._view=null,this._mode=e,this._applyView(this._zoom,t?.x,t?.y)}l&&(h=1,d=void 0,p=void 0)}if("plan"===s&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=e,this._editorChromeMode="view"===e?s:e,"view"===e){"view"===s||a||(this._modeTransitionEditorCamera={zoom:this._zoom,centerX:r?.viewport.centerX,centerY:r?.viewport.centerY});const e=this._viewModeSnap;if(e&&e.space===this._space){h=e.zoom;const t=this._labsIso&&"iso"===this._viewPreference[this._space]?"iso":"flat",i=null!=e.cx&&null!=e.cy&&"iso"===t?sf([e.cx,e.cy],0):null;d=i?.[0]??e.cx,p=i?.[1]??e.cy}else e&&(h=this._zoomBySpace[this._space]||1,d=void 0,p=void 0)}else this._modeTransitionEditorCamera={zoom:h,centerX:d,centerY:p};this._modeTransitionTargetZoom=h,this._modeTransitionTargetCenterX=d,this._modeTransitionTargetCenterY=p,("iso"===n||"view"===e&&this._labsIso&&"iso"===this._viewPreference[this._space])&&(this._modeTransitionForceAtomic=!0);const u=++this._modeTransitionRequest;t?r?(this._modeTransitionPreparing=!0,this._modeTransitionVisual=r,this._prepareModeTransition(u,r,e,h,d,p)):(this._modeTransitionPreparing=!1,this._modeTransitionVisual=null,this._zoom=h,this._view=null,requestAnimationFrame(()=>{this.isConnected&&u===this._modeTransitionRequest&&this._mode===e&&(this._applyView(h,d,p),"view"===e&&(this._viewModeSnap=null,this._modeTransitionEditorCamera=null),this.requestUpdate())})):"view"===e&&this._commitViewModeAtomic(r,h,d,p),this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!0),this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszResetController(),this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===e&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=bl(ul,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?bl(ul,this._imperial):this._drawWallField}get _drawWallCm(){const e=Kf(this._drawWallFieldValue);if(null==e||e<0)return null;const t=this._imperial?2.54*e:e,i="column"===this._tool?Yc:100;return t>=("column"===this._tool?1:0)&&t<=i?t:null}get _drawWallMaxCm(){return"column"===this._tool?Yc:100}_showPhysicalRange(e=this._drawWallMaxCm,t=0){this._showToast(this._t("toast.physical_range",{min:bl(t,this._imperial),max:bl(e,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(e=this._curSpaceCfg){return(e?.room_drafts||[]).reduce((e,t)=>e+(Array.isArray(t.segments)?t.segments.length:0),0)}_mergeSpacePartitions(e,t){const i=e?.partitions||[];if(i.length<2)return 0;const s=cm(i,{pitch:Fh,seedIds:t,geometry:dm(e,{excludeDraftId:this._activeDraftId})});return s.merged?(e.partitions=s.partitions,hm(e.openings,e.partitions,s.openingMoves,{coordScale:lv,cellCm:this._cellCm,gridPitch:this._gridPitch}),s.merged):0}_finishWallChain(){if("draw"!==this._tool||this._wallFaceBatch||this._roomDialog)return!0;const e=this._curSpaceCfg;if(!e||this._path.length<2)return this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),!0;const t=su(this._path,iu(this._path.length-1,this._draftSegmentCms,this._drawWallCm,ul)),i=t.length;if((e.partitions||[]).length+i>pv)return this._showToast(this._t("toast.physical_limit")),!1;const s=this._geometrySnapshot();e.partitions||=[];const n=Date.now().toString(36),o=[],a=this._activeDraftId?(e.room_drafts||[]).find(e=>e.id===this._activeDraftId):null,r=this._draftSegmentsForPath(this._path,a,t.map(e=>e.cm));for(let s=0;se.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),this._commitPhysicalGeometry(this._t("history.wall_chain_finish"),s),delete this._resumeDraftBySpace[this._space],this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),!0}_activateMarkupTool(e){e!==this._tool&&(this._wallFaceBatch&&this._roomDialogCancel(),("draw"!==this._tool||this._finishWallChain())&&(this._cancelPath(),"resize"===this._tool&&(this._resize.dragging?this._rszCancelDrag():this._resize.reset()),this._tool=e,"draw"===e&&this._resumeLastDraft(),"resize"===e&&this._resize.selectRoom(null),"wallthick"===e&&(this._wallDialog=null)))}_limitReached(e){const t=this._curSpaceCfg;if(!t)return!0;const i="draft"===e?(t.room_drafts||[]).length>=200:"partition"===e?(t.partitions||[]).length>=pv:(t.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(e){const t=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(e.clientX-t.left,e.clientY-t.top)}_snap(e){const t=this._gridPitch;return[Oh(ri(e[0],t)),Oh(ri(e[1],t))]}_snapDrawPoint(e,t=!1){const i=this._path[this._path.length-1],s=t&&i?function(e,t,i,s=1/0){if(!(i>0&&e?.every(Number.isFinite)&&t?.every(Number.isFinite)))return[t[0],t[1]];const n=t[0]-e[0],o=t[1]-e[1];if(Math.abs(n)+Math.abs(o)<=1e-12)return[e[0],e[1]];const a=Math.PI/4;let r=Math.atan2(o,n);r<0&&(r+=2*Math.PI);const l=Math.floor(r/a+.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 p=Math.max(0,Math.round((n*c+o*h)/(i*d)));if(Number.isFinite(s)&&s>=0){const t=(e,t)=>{if(!t)return 1/0;const n=t>0?s-e:e+s;return Math.max(0,Math.floor((n+1e-9*i)/i))};p=Math.min(p,t(e[0],c),t(e[1],h))}return[e[0]+c*p*i,e[1]+h*p*i]}(i,e,this._gridPitch,Rh):e;return this._snap(s)}_planSnapOpeningCuts(e,t){if(!this._openingsR.length)return[];const i=this._openingWallIndexFor(e,t).value,s=[];for(const t of this._roomWallOpeningInputs(this._openingsR,e)){const e=jc(i,t);if(!e.negative&&!e.positive)continue;const n=t.angle*Math.PI/180,o=Math.cos(n)*t.length/2,a=Math.sin(n)*t.length/2;s.push([t.x-o,t.y-a,t.x+o,t.y+a])}return s}_planSnapGeometrySnapshot(){const e=this._spaceModel();if(!e)return{key:`${this._space}|empty`,value:{segments:[],endpoints:[]}};const t=[this._space,this._cfgEpoch,this._activeDraftId||"",e.rooms.length,e.room_drafts.length,e.partitions.length].join("|");if(this._planSnapGeometryCache?.key===t)return this._planSnapGeometryCache;const i=this._openCuts(),s=Kp({space:e,activeDraftId:this._activeDraftId,roomCuts:this._planSnapOpeningCuts(e,i),partitionCuts:this._partitionOpeningCuts(e),epsilon:2e-4*this._gridPitch});return this._planSnapGeometryCache={key:t,value:s},this._planSnapGeometryCache}_hiddenWallDiagnosticSnapshot(){const e=this._spaceModel();if(!e)return{key:`${this._space}|hidden-empty`,value:{segments:[],endpoints:[]}};const t=["hidden",this._space,this._cfgEpoch,this._activeDraftId||"",e.rooms.length,e.room_drafts.length,e.partitions.length].join("|");if(this._hiddenWallDiagnosticCache?.key===t)return this._hiddenWallDiagnosticCache;const i=Vp({space:e,activeDraftId:this._activeDraftId,epsilon:2e-4*this._gridPitch});return this._hiddenWallDiagnosticCache={key:t,value:i},this._hiddenWallDiagnosticCache}_planStructuralGeometrySnapshot(){const e=this._spaceModel();if(!e)return{key:`${this._space}|structural-empty`,value:{segments:[],endpoints:[]}};const t=["structural",this._space,this._cfgEpoch,this._activeDraftId||"",e.rooms.length,e.room_drafts.length,e.partitions.length].join("|");if(this._planStructuralGeometryCache?.key===t)return this._planStructuralGeometryCache;const i=Kp({space:e,activeDraftId:this._activeDraftId,epsilon:2e-4*this._gridPitch});return this._planStructuralGeometryCache={key:t,value:i},this._planStructuralGeometryCache}_planSnapContextKey(e){const t=this._path[0],i=this._path[this._path.length-1];return[e,this._tool,this._path.length,t?`${t[0]},${t[1]}`:"",i?`${i[0]},${i[1]}`:""].join("|")}_resolvePlanDrawPoint(e,t){const i=this._planSnapGeometrySnapshot(),s=this._path[this._path.length-1],n="draw"===this._tool&&this._path.length>=3?[{point:this._path[0],key:"closure:first-point"}]:[],o={tolerance:this._cssPxToRender(12),distinguishTolerance:this._cssPxToRender(8),gridStep:this._gridPitch,excludePoints:s?[s]:[],extraEndpoints:n,epsilon:2e-4*this._gridPitch},a=t&&s?eu(i.value,e,{...o,anchor:s}):function(e,t,i){if(!(Hp(t)&&i.tolerance>=0))return{kind:"none",candidate:null,conflicts:[]};const s=i.epsilon??Op,n=i.excludePoints||[],o=Zp(Xp(e,t,i),i.distinguishTolerance||0);if(o)return o;let a=null;for(const o of e.segments){const e=[o.a[0],o.a[1],o.b[0],o.b[1]],r=Hs([t[0],t[1]],e);if(r>i.tolerance)continue;const l=Qp(o,t,i.gridStep);!Yp(l,n,s)&&Jp(r,o.key,a)&&(a={kind:"line",point:l,key:o.key,distance:r,segment:o})}return a?{kind:"resolved",candidate:a,conflicts:[]}:{kind:"none",candidate:null,conflicts:[]}}(i.value,e,o),r="resolved"===a.kind?a.candidate:null,l=r?[...r.point]:this._snapDrawPoint(e,t),c=s?ua(s,l):l;return{point:c,candidate:r&&fi(c,r.point)?r:null,conflicts:"ambiguous"===a.kind?a.conflicts:[],ambiguous:"ambiguous"===a.kind,contextKey:this._planSnapContextKey(i.key)}}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){this._planSnapHover=null,this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers([]),e&&(this._cursorPt=null)}_samePt(e,t){return fi(e,t)}_dropLegacySegments(){for(const e of this._serverCfg?.markers||[])"ripple"===e.display&&(e.display="icon_ripple");for(const e of this._serverCfg?.spaces||[]){delete e.segments;const t=new Set,i=e=>"string"==typeof e&&e.length>=1&&e.length<=64&&!t.has(e),s=e=>(t.add(e),!0),n=e=>Array.isArray(e)&&2===e.length&&e.every(e=>Number.isFinite(Number(e))&&Math.abs(Number(e))<=Ih);if(Array.isArray(e.partitions)&&(e.partitions=e.partitions.filter(e=>e&&i(e.id)&&n(e.a)&&n(e.b)&&Math.hypot(e.a[0]-e.b[0],e.a[1]-e.b[1])>1e-9&&s(e.id)).map(e=>({...e,cm:Number.isFinite(Number(e.cm))?Math.max(0,Math.min(100,Number(e.cm))):15})),e.partitions.length||delete e.partitions),Array.isArray(e.wall_columns)&&(e.wall_columns=e.wall_columns.filter(e=>e&&i(e.id)&&n(e.center)&&s(e.id)).map(e=>({id:e.id,shape:"circle"===e.shape?"circle":"square",center:[Number(e.center[0]),Number(e.center[1])],cm:Xc(Number(e.cm)||15),..."circle"===e.shape?{}:{angle:Zc(e.angle)}})),e.wall_columns.length||delete e.wall_columns),Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e&&i(e.id)&&Array.isArray(e.points)&&e.points.length>=2&&e.points.every(n)&&s(e.id)).map(e=>{const t=Lm(e);return{id:e.id,...t}}).filter(e=>e.points.length>=2),e.room_drafts.length||delete e.room_drafts),Array.isArray(e.walls)){const t=Td(Th({spaces:[e]}),e.id),i=t?w_(e,t,lv,.02*zh).contour.map(e=>e.map(e=>e/lv)):vd(e.open_spans).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]]);e.walls=Fl(e.walls,e.rooms||[],Fh,1,i),e.walls.length||delete e.walls}}}_rollbackRejectedPhysicalWrites(e){if(!this._serverCfg||!e.length)return!1;let t=!1;for(const[i]of e){const e=this._pendingPhysicalWrites.get(i);e&&(t=this._restoreGeometryStateInConfig(this._serverCfg,e.before)||t,this._pendingPhysicalWrites.delete(i))}return!!t&&(this._clearGeometryGesture(),this._geometryHistory.clear(),this._cfgEpoch++,this._modelCache=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._frame=null,this._regSignature="",this._cfgContentFingerprint=Md(this._serverCfg),this._maybeRebuildDevices(),this.requestUpdate(),!0)}async _reloadRejectedPhysicalWrite(){const e=this._writeChain;await e.catch(()=>{}),await this._reloadConfigOnly(!0)}get _cfgWriting(){return this._writesPending>0}_writeConfig(){var e,t;this._writesPending++,this._writeChain=(e=this._writeChain,t=async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const e=pl(Xr(this._serverCfg)),t=Md(e),i=[...this._pendingPhysicalWrites.entries()];for(const[t,s]of i){const i=mg(e.spaces.find(e=>e.id===t));if(i===s.fingerprint)continue;let n=!1;try{n=this._checkSpacePhysicalGeometry(e,t).ok}catch{n=!1}if(!n)throw this._restoreGeometryStateLocal(s.before),this._pendingPhysicalWrites.delete(t),this._geometryHistory.clear(),this._showToast(this._t("toast.geometry_unsafe")),Object.assign(new Error("unsafe wall geometry"),{code:"geometry-unsafe"});s.fingerprint=i}let s;t!==Md(this._serverCfg)&&(this._serverCfg=e),this._cfgContentFingerprint=t;try{s=await this.hass.callWS({type:"houseplan/config/set",config:e,expected_rev:this._cfgRev})}catch(e){if(!this._rollbackRejectedPhysicalWrites(i))throw e;const t=e&&"object"==typeof e?e:Object.assign(new Error(String(e)),{cause:e});throw t.physicalGeometryRolledBack=!0,t}this._cfgRev=s?.rev??this._cfgRev+1;for(const[e,t]of i)this._pendingPhysicalWrites.get(e)?.fingerprint===t.fingerprint&&this._pendingPhysicalWrites.delete(e)},e.catch(()=>{}).then(t));return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshotFromConfig(e,t){const i=e?.spaces?.find(e=>e.id===t);if(!i)return null;const s=e=>JSON.parse(JSON.stringify(e)),n={};for(const e of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==i[e]&&(n[e]=i[e]);return{spaceId:t,rooms:s(i.rooms||[]),...Array.isArray(i.openings)?{openings:s(i.openings)}:{},...Array.isArray(i.walls)?{walls:s(i.walls)}:{},...Array.isArray(i.wall_segments)?{wall_segments:s(i.wall_segments)}:{},...Array.isArray(i.open_spans)?{open_spans:s(i.open_spans)}:{},...Array.isArray(i.room_drafts)?{room_drafts:s(i.room_drafts)}:{},...Array.isArray(i.partitions)?{partitions:s(i.partitions)}:{},...Array.isArray(i.wall_columns)?{wall_columns:s(i.wall_columns)}:{},...Array.isArray(i.decor)?{decor:s(i.decor)}:{},plan_transform:n}}_geometrySnapshot(e=this._space){return this._geometrySnapshotFromConfig(this._serverCfg,e)}_recordGeometry(e,t){if(!t)return;const i=this._geometrySnapshot(t.spaceId);i&&JSON.stringify(t)!==JSON.stringify(i)&&(this._geometryHistory.push({name:e,before:t,after:i}),this.requestUpdate())}_restoreGeometryStateInConfig(e,t,i=!1){const s=e?.spaces?.find(e=>e.id===t.spaceId);if(!s)return!1;const n=e=>JSON.parse(JSON.stringify(e)),o=new Map((s.rooms||[]).map(e=>[e.id,e]));if(s.rooms=n(t.rooms),i)for(const e of s.rooms){const t=o.get(e.id);!Array.isArray(e.wall_ids)&&Array.isArray(t?.wall_ids)&&t.wall_ids.length===e.poly?.length&&(e.wall_ids=n(t.wall_ids))}const a=(e,t)=>{void 0!==t?s[e]=n(t):i&&"wall_segments"===e||delete s[e]},r=new Map((s.openings||[]).map(e=>[e.id,e])),l=new Map((s.room_drafts||[]).map(e=>[e.id,e]));if(a("openings",t.openings),a("walls",t.walls),a("wall_segments",t.wall_segments),a("open_spans",t.open_spans),a("room_drafts",t.room_drafts),a("partitions",t.partitions),a("wall_columns",t.wall_columns),a("decor",t.decor),i){for(const e of s.openings||[]){const t=r.get(e.id);e.host||"wall"!==t?.host?.kind||(e.host=n(t.host))}for(const e of s.room_drafts||[]){const t=l.get(e.id);for(let i=0;i<(e.segments||[]).length;i++)!e.segments[i]?.id&&t?.segments?.[i]?.id&&(e.segments[i].id=t.segments[i].id)}}for(const e of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete s[e];return Object.assign(s,n(t.plan_transform||{})),!0}_restoreGeometryStateLocal(e){return!!this._restoreGeometryStateInConfig(this._serverCfg,e)&&(this._cfgEpoch++,this._modelCache=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._frame=null,this.requestUpdate(),!0)}_wallModelBlockerLabel(e){const t=e instanceof Rm?e.reason:"invalid-room";return this._t(`wall_model.reason.${t}`)}_hasLegacyZeroWallFields(e=this._serverCfg){return(e?.spaces||[]).some(e=>Array.isArray(e?.open_spans)&&e.open_spans.length>0||(e?.rooms||[]).some(e=>Array.isArray(e?.open_to)&&e.open_to.length>0))}_showWallModelMigrationBlocked(e){const t=this._hasLegacyZeroWallFields()?"toast.zero_wall_migration_blocked":"toast.wall_model_migration_blocked";this._showToast(this._t(t,{reason:this._wallModelBlockerLabel(e)}))}_commitPhysicalGeometry(e,t,i=[]){if(!t||!this._serverCfg)return!1;const s=this._serverCfg,n=this._geometrySnapshotFromConfig(s,t.spaceId),o=s.spaces.find(e=>e.id===t.spaceId);if(!o||mg(t)===mg(o))return!1;let a,r=!1;try{r=this._checkSpacePhysicalGeometry(s,t.spaceId).ok}catch{r=!1}if(!r)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(t),this._showToast(this._t("toast.geometry_unsafe")),!1;let l=t;try{if(Number(s.model_version||0)<9){const e=JSON.parse(JSON.stringify(s));if(!this._restoreGeometryStateInConfig(e,t))throw new Rm("invalid-room",t.spaceId);const i=sg(e).config;l=this._geometrySnapshotFromConfig(i,t.spaceId)||t;const o=JSON.parse(JSON.stringify(i));if(!n||!this._restoreGeometryStateInConfig(o,n,!0))throw new Rm("invalid-room",t.spaceId);const r=i.spaces.find(e=>e.id===t.spaceId),c=o.spaces.find(e=>e.id===t.spaceId);if(!Array.isArray(n.wall_segments)&&JSON.stringify(t.rooms)===JSON.stringify(n.rooms))for(const e of c?.wall_segments||[])e.cm=zl(n.walls,e.a,e.b,Fh,1);const h=((e,t,i)=>{const s=new Map,n=new Map((e?.rooms||[]).map(e=>[e.id,e])),o=new Map((t||[]).map(e=>[e.id,e])),{rooms:a}=Zm(i,Jm(e));for(const e of a){const t=o.get(e.id),a=n.get(e.id),r=hi(t),l=hi((i.rooms||[]).find(t=>t.id===e.id)),c=hi(a);if(r&&l&&c&&r.length===l.length&&c.length===a?.wall_ids?.length&&e.poly.length===e.wall_ids.length)for(let t=0;t{const n=[];for(let o=0;oIm||Wm(r,i,s)>Im)continue;const l=qm(a,i,s),c=qm(r,i,s);l<-Im||c>1+Im||c<=l+Im||n.push({start:l,id:t[o]})}return n.sort((e,t)=>e.start-t.start)},n=i(c,a.wall_ids,r[t],r[(t+1)%r.length]),o=i(e.poly,e.wall_ids,l[t],l[(t+1)%l.length]);if(n.length&&n.length===o.length)for(let e=0;ee.id===t.spaceId),s=this._path.length>=2?this._path.map(e=>[e[0]/lv,e[1]/lv]):[];s.push(...i.map(e=>[e[0],e[1]])),c=Bm(e)<=Bm(l,s)&&this._checkSpacePhysicalGeometry(a,t.spaceId).ok}catch{c=!1}if(!c)return this._clearGeometryGesture(),this._restoreGeometryStateLocal(t),this._showToast(this._t("toast.geometry_unsafe")),!1;og(s,a),this._recordGeometry(e,l);const h=s.spaces.find(e=>e.id===t.spaceId),d=this._pendingPhysicalWrites.get(t.spaceId);return this._pendingPhysicalWrites.set(t.spaceId,{before:d?.before||l,fingerprint:mg(h)}),this._saveConfig(),!0}_clearGeometryGesture(){this._path=[],this._clearPlanSnapHover(),this._clearOpeningPlacement(!1),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._rszResetController(),this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(e){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===e.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid===e.pointerId||this._physicalRotate?.pid===e.pointerId)return void this._cancelPhysicalGesture();if(this._decorDraft?.pid===e.pointerId)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove?.pid===e.pointerId||this._dtDrag?.pid===e.pointerId||this._bdDrag?.pid===e.pointerId)return void this._cancelDecorGesture();"opening"===this._tool?(this._cursorPt=null,this._clearOpeningPlacement(!1)):"draw"===this._tool&&this._clearPlanSnapHover();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),t&&0===this._pointers.size&&this.requestUpdate()}_applyGeometryState(e,t=!1){if(!this._canCommitSpace(e.spaceId))return!1;const i=this._geometrySnapshot(e.spaceId);if(!i||!this._restoreGeometryStateLocal(e))return!1;const s=this._serverCfg,n=mg(i)!==mg(e);if(n){let n=!1;try{const i=s?this._checkSpacePhysicalGeometry(s,e.spaceId):null;n=!!i?.ok||!(!t||"wall-degraded-extra"!==i?.reason)}catch{n=!1}if(!n)return this._restoreGeometryStateLocal(i),this._showToast(this._t("toast.geometry_unsafe")),!1}let o;try{o=sg(s).config}catch(e){return this._restoreGeometryStateLocal(i),this._showWallModelMigrationBlocked(e),!1}if(n){let n=!1;try{const i=o?this._checkSpacePhysicalGeometry(o,e.spaceId):null;n=!!i?.ok||!(!t||"wall-degraded-extra"!==i?.reason)}catch{n=!1}if(!n)return this._restoreGeometryStateLocal(i),this._showToast(this._t("toast.geometry_unsafe")),!1;og(s,o);const a=s?.spaces.find(t=>t.id===e.spaceId),r=this._pendingPhysicalWrites.get(e.spaceId);this._pendingPhysicalWrites.set(e.spaceId,{before:r?.before||i,fingerprint:mg(a)})}else og(s,o);return this._clearGeometryGesture(),this._space!==e.spaceId&&(this._commitSpace(e.spaceId),this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(e){return this._spaceModel()?.rooms.find(t=>{const i=hi(t);return!!i&&wi(e,i)})}_overlapRoom(e){return this._spaceModel()?.rooms.find(t=>{const i=hi(t);return!!i&&Di(e,i)})}_pointInRoom(e,t){return t.poly?vi(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){if(!Ra(e))return!0;const t=e.length,i=.001;for(let s=0;s=500)return this._showToast(this._t("toast.physical_limit")),!1;const e=this._curSpaceCfg;return!(!this._activeDraftId&&(e?.room_drafts||[]).length>=200||this._draftSegmentCount(e)>=2e3||(e?.partitions||[]).length+this._path.length>pv)||(this._showToast(this._t("toast.physical_limit")),!1)}_markupClick(e){if(this._vacFit)return;if(!this._markup)return;const t=this._spaceModel();if(!t)return void this._clearGeometryGesture();if(this._suppressClick)return;if(this._drag||this._rlResize)return;const i=e.composedPath?.()||[];if(i.some(e=>e?.classList?.contains?.("roomlabel")||e?.classList?.contains?.("rlhandle")))return;if(i.some(e=>e?.classList?.contains?.("physical-hit")))return;const s=this._svgPoint(e);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._resize.dragging||i.some(e=>e?.classList?.contains?.("rszhandle")))return;const e=[...t.rooms].reverse().find(e=>this._pointInRoom(s,e));return this._resize.selectRoom(e?.id||null),void this.requestUpdate()}if("delroom"===this._tool)return void this._deleteRoomClick(s);if("opening"===this._tool)return void this._openingClick(s);if("merge"===this._tool)return void this._mergeClick(s);if("wallthick"===this._tool)return void this._wallThickClick(s);if("split"===this._tool)return void this._splitClick(s);if("column"===this._tool)return void this._columnClick(s);this._wallRepairDiagnostic=null;const n=this._resolvePlanDrawPoint(s,e.shiftKey);if(n.ambiguous)return this._planSnapHover={contextKey:n.contextKey,candidate:null,conflicts:n.conflicts},this._syncPlanSnapActiveMarker(null),this._syncPlanSnapConflictMarkers(n.conflicts),void this._showToast(this._t("toast.plan_snap_ambiguous"));let o=n.point;if(e.ctrlKey||e.metaKey){if(e.preventDefault(),this._path.length<3)return;o=ua(this._path[this._path.length-1],this._path[0])}if(!this._path.length){if(!e.shiftKey&&!n.candidate&&this._offerExistingWallFace(s))return;const t=this._draftEndAt(o);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(e=>[...e]):t.draft.points.map(e=>[...e]),void(this._draftSegmentCms=this._adoptDraftCms(this._path,t.reverse?[...t.draft.segments].reverse().map(e=>e.cm):t.draft.segments.map(e=>e.cm),t.draft.id))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[o]))}const a=this._path[this._path.length-1];if(this._samePt(o,a))return;const r=this._draftEndAt(o,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(!this._canAppendRoomDraftPoint())return;const l=this._drawWallCm;if(null==l)return void this._showPhysicalRange(100);const c=this._path.map(e=>[...e]);this._path=[...this._path,o],this._draftSegmentCms=[...this._draftSegmentCms,l],this._persistActiveDraftSegment(),this._offerWallFaces(c)}_draftEndAt(e,t){const i=this._spaceModel();if(!i)return null;const s=this._viewOr(this._baseVb()),n=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?s.w/this._stageEl.clientWidth*12:0);for(const s of i.room_drafts||[]){if(s.id===t)continue;if(s.points.length<2)continue;const i=s.points[0],o=s.points[s.points.length-1];if(Math.hypot(e[0]-o[0],e[1]-o[1])<=n)return{draft:s,reverse:!1};if(Math.hypot(e[0]-i[0],e[1]-i[1])<=n)return{draft:s,reverse:!0}}return null}_mergeDraftEndpoint(e){const t=this._curSpaceCfg;if(!t||!this._path.length)return;const i=this._path.map(e=>[...e]),s=this._wallGraphSources(i),n=Array.isArray(t.room_drafts)?t.room_drafts:[],o=this._activeDraftId?n.find(e=>e.id===this._activeDraftId):null,a=n.find(t=>t.id===e.draft.id);if(!a)return;const r=e.reverse?e.draft.points.map(e=>[...e]):[...e.draft.points].reverse().map(e=>[...e]),l=e.reverse?(a.segments||[]).map(e=>({...e})):[...a.segments||[]].reverse().map(e=>({...e})),c=this._path[this._path.length-1],h=this._samePt(c,r[0]),d=h?null:this._drawWallCm;if(!h&&null==d)return void this._showPhysicalRange(100);const p=this._draftSegmentsForPath(this._path,o,this._draftSegmentCms),u=[...this._path.map(e=>[...e]),...h?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._showToast(this._t("toast.physical_limit"));const v=(o?.segments?.length||0)+(a.segments?.length||0);if(this._draftSegmentCount(t)-v+f.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(m){const e=g.slice(0,-1);if(this._contourSelfIntersects(e)||Ti(e)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(e);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const y=this._geometrySnapshot(),b=this._activeDraftId||e.draft.id,w={...o||a,id:b,points:g.map(e=>[e[0]/lv,e[1]/lv]),segments:f};t.room_drafts=n.filter(t=>t.id!==e.draft.id&&(!this._activeDraftId||t.id!==this._activeDraftId)),t.room_drafts.push(w),this._activeDraftId=b,this._resumeDraftBySpace[this._space]=b,this._path=g,this._draftSegmentCms=this._adoptDraftCms(g,f.map(e=>Number(e.cm)),b),this._physicalSel=null,this._commitPhysicalGeometry(this._t("history.draft_merge"),y),null!=d&&this._offerWallFaces(i,i.length-1,s)}_adoptDraftCms(e,t,i){const s=Math.max(0,e.length-1),n=iu(s,t,this._drawWallCm,ul);return t.length!==s&&console.debug(`[houseplan] draft ${i??"?"}: восстановлено толщин ${s-t.length} (#234)`),n}_draftSegmentsForPath(e,t,i){const s=Array.isArray(t?.points)?t.points.map(e=>[Number(e?.[0])*lv,Number(e?.[1])*lv]):[],n=Array.isArray(t?.segments)?t.segments:[];return iu(Math.max(0,e.length-1),i,this._drawWallCm,ul).map((t,i)=>{const o=e[i],a=e[i+1],r=s.findIndex((e,t)=>{if(t+1>=s.length)return!1;const i=s[t+1];return this._samePt(o,e)&&this._samePt(a,i)||this._samePt(o,i)&&this._samePt(a,e)});return{...r>=0?n[r]:{},cm:t}})}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const e=this._geometrySnapshot(),t=this._curSpaceCfg;t.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const i=t.room_drafts.findIndex(e=>e.id===this._activeDraftId),s={...i>=0?t.room_drafts[i]:{},id:this._activeDraftId,points:this._path.map(e=>[e[0]/lv,e[1]/lv]),segments:this._draftSegmentsForPath(this._path,i>=0?t.room_drafts[i]:null,this._draftSegmentCms)};i>=0?t.room_drafts[i]=s:t.room_drafts.push(s),this._commitPhysicalGeometry(this._t("history.draft_segment"),e)}_activeWallSourceKey(e){return`active:${this._activeDraftId||"session"}:${e}`}_wallGraphSources(e){const t=this._planStructuralGeometrySnapshot().value.segments.map(e=>({a:e.a,b:e.b,key:`static:${e.key}`}));for(let i=0;i+1e.key===i);if(s>=0){const[e]=this._wallFaceGraphCache.splice(s,1);return this._wallFaceGraphCache.push(e),e.value}const n=Su(e,t);return this._wallFaceGraphCache.push({key:i,value:n}),this._wallFaceGraphCache.length>4&&this._wallFaceGraphCache.shift(),n}_offerWallFaces(e,t=this._path.length-2,i){if(this._path.length<2||this._wallFaceBatch||this._roomDialog)return;const s=this._spaceModel();if(!s)return;const n=this._activeWallSourceKey(t),o=2e-4*this._gridPitch;let a,r;try{const t=i||this._wallGraphSources(e);r=this._wallGraphSources(this._path),a=function(e,t,i){const s=new Set(e.faces.map(e=>e.key));return t.faces.filter(e=>!s.has(e.key)&&e.sourceKeys.includes(i))}(this._wallFaceGraph(t,o),this._wallFaceGraph(r,o),n)}catch{return}if(!a.length){const e=Nu(r,{requiredSourceKey:n,maxDistance:wl(2,this._cellCm,this._gridPitch),gridStep:this._gridPitch,epsilon:o});return"ambiguous"===e.kind?(this._wallRepairDiagnostic=e.proposals[0]||null,void this._showToast(this._t(0===this._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous"))):void("repair"!==e.kind||this._overlapRoom(e.face.ring)||this._beginWallFaceBatch([{...e.face,repair:e.proposal}]))}for(const e of s.rooms){const t=hi(e);if(!e.id||!t)continue;const i=zi(t,this._path,.02*this._gridPitch);if(!i)continue;const[s,n]=i,o=Ti(s)>=Ti(n)?s:n,r=o===s?n:s,l=[...a].sort((e,t)=>Math.abs(e.area-Ti(r))-Math.abs(t.area-Ti(r))||e.key.localeCompare(t.key))[0];if(!l)return;return void this._beginWallFaceBatch([{...l,ring:r.map(e=>[e[0],e[1]]),split:{roomId:e.id,mainPoly:o,newPoly:r},consumeAllActive:!0}])}const l=a.filter(e=>!(e.ring.length<3||this._contourSelfIntersects(e.ring))&&!this._overlapRoom(e.ring));l.length&&this._beginWallFaceBatch(l)}_beginWallFaceBatch(e){this._wallRepairDiagnostic=null,this._wallFaceBatch={candidates:[...e].sort((e,t)=>e.area-t.area||e.key.localeCompare(t.key)),index:0,decisions:[],activePath:this._path.map(e=>[...e]),activeCms:[...this._draftSegmentCms],activeDraftId:this._activeDraftId},this._clearPlanSnapHover(),this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0}_offerExistingWallFace(e){if(this._path.length||this._wallFaceBatch||this._roomDialog)return!1;const t=2e-4*this._gridPitch;try{let i,s=Mu(this._wallFaceGraph(this._wallGraphSources([]),t),e,t);if(!s){const n=Nu(this._wallGraphSources([]),{point:e,maxDistance:wl(2,this._cellCm,this._gridPitch),gridStep:this._gridPitch,epsilon:t});if("ambiguous"===n.kind)return this._wallRepairDiagnostic=n.proposals[0]||null,this._showToast(this._t(0===this._drawWallCm?"toast.zero_wall_ambiguous":"toast.wall_repair_ambiguous")),!0;if("repair"===n.kind)s=n.face,i=n.proposal;else if("none"===n.kind){const i=Nu(this._wallGraphSources([]),{point:e,maxDistance:this._cssPxToRender(12),gridStep:this._gridPitch,epsilon:t});if("repair"===i.kind)return this._wallRepairDiagnostic=i.proposal,this._showToast(this._t("toast.wall_repair_too_large")),!0}}return!(!s||s.ring.length<3||this._contourSelfIntersects(s.ring)||this._overlapRoom(s.ring))&&(this._beginWallFaceBatch([{...s,existing:!i,repair:i}]),!0)}catch{return!1}}_columnClick(e){const t=this._snap(e),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Yc,1);if(!this._curSpaceCfg||this._limitReached("column"))return;const s=this._spaceModel();if(!s)return;const n={id:"column-"+Date.now().toString(36),shape:"square",center:t,cm:Xc(i),angle:0},o=(s.wall_columns||[]).find(e=>vh(e,n,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const a=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...n,center:[t[0]/lv,t[1]/lv]}),this._commitPhysicalGeometry(this._t("history.column_add"),a)}_openPhysicalDialog(e,t,i){const s=this._spaceModel();if(s)if("partition"===e){const i=s.partitions.find(e=>e.id===t);i&&(this._physicalDialog={kind:e,id:t,cm:bl(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===e){const i=s.wall_columns.find(e=>e.id===t);i&&(this._physicalDialog={kind:e,id:t,cm:bl(i.cm,this._imperial),shape:i.shape,angle:this._angleField("square"===i.shape?Zc(i.angle):0)})}else{const n=s.room_drafts.find(e=>e.id===t),o=Math.max(0,Math.min(n?.segments.length?n.segments.length-1:0,i||0));n?.segments[o]&&(this._physicalDialog={kind:e,id:t,segment:o,cm:bl(n.segments[o].cm,this._imperial),length:this._fmtLen(n.points[o],n.points[o+1])})}}_physicalDown(e,t,i){const s=this._spaceModel();if(!s)return;e.stopPropagation(),gv(e);const n=this._svgPoint(e),o=[];for(const e of[...s.wall_columns].reverse())gh(n,nh(e,this._cellCm,this._gridPitch))&&o.push({kind:"column",id:e.id});for(const e of[...s.partitions].reverse()){const t=sh(e.a,e.b,e.cm,this._cellCm,this._gridPitch);t&&gh(n,t)&&o.push({kind:"partition",id:e.id})}o.some(e=>e.kind===t&&e.id===i)||o.unshift({kind:t,id:i});const a=o.map(e=>`${e.kind}:${e.id}`).sort().join("|"),r=performance.now(),l=this._physicalPickCycle,c=o.length>1&&l?.signature===a&&r-l.at>380&&r-l.at<=1200&&Math.hypot(e.clientX-l.x,e.clientY-l.y)<=10?(l.index+1)%o.length:Math.max(0,o.findIndex(e=>e.kind===t&&e.id===i));this._physicalPickCycle={signature:a,index:c,x:e.clientX,y:e.clientY,at:r},t=o[c].kind,i=o[c].id;const h="partition"===t?s.partitions.find(e=>e.id===i):s.wall_columns.find(e=>e.id===i);h&&(this._physicalSel={kind:t,id:i},this._physicalDrag={pid:e.pointerId,kind:t,id:i,start:this._svgPoint(e),startClient:[e.clientX,e.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(h)),delta:[0,0]})}_clampPhysicalDelta(e,t,i){const s="partition"===e?[t.a,t.b]:[t.center],n=s.map(e=>e[0]),o=s.map(e=>e[1]);return[Math.max(-Ih*lv-Math.min(...n),Math.min(Ih*lv-Math.max(...n),i[0])),Math.max(-Ih*lv-Math.min(...o),Math.min(Ih*lv-Math.max(...o),i[1]))]}_physicalMove(e){const t=this._physicalDrag;if(!t||t.pid!==e.pointerId)return;e.stopPropagation();const i=this._svgPoint(e),s="partition"===t.kind?t.base.a:t.base.center,n=this._snap([s[0]+i[0]-t.start[0],s[1]+i[1]-t.start[1]]),o=this._clampPhysicalDelta(t.kind,t.base,[n[0]-s[0],n[1]-s[1]]);this._physicalDrag={...t,delta:o,moved:t.moved||Math.hypot(e.clientX-t.startClient[0],e.clientY-t.startClient[1])>=5}}_physicalUp(e){const t=this._physicalDrag;if(!t||t.pid!==e.pointerId)return;if(e.stopPropagation(),this._physicalDrag=null,!t.moved)return void this._registerPhysicalTap(t.kind,t.id);const i=this._spaceModel();if(!this._curSpaceCfg||!i)return;const s=this._curSpaceCfg;if("partition"===t.kind){const e=(s.partitions||[]).find(e=>e.id===t.id),i=t.base;if(e){const n={...i,a:[i.a[0]+t.delta[0],i.a[1]+t.delta[1]],b:[i.b[0]+t.delta[0],i.b[1]+t.delta[1]]};e.a=[n.a[0]/lv,n.a[1]/lv],e.b=[n.b[0]/lv,n.b[1]/lv];for(const e of s.openings||[]){if("partition"!==e.host?.kind||e.host.id!==t.id)continue;const i=Tp(e,[n],lv,this._cellCm,this._gridPitch).resolved;i&&Object.assign(e,Np(e,i,lv))}}}else{const e=(s.wall_columns||[]).find(e=>e.id===t.id),n=t.base,o={...n,center:[n.center[0]+t.delta[0],n.center[1]+t.delta[1]]};if(i.wall_columns.some(e=>e.id!==t.id&&vh(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));e&&(e.center=[(n.center[0]+t.delta[0])/lv,(n.center[1]+t.delta[1])/lv])}this._commitPhysicalGeometry(this._t("history.physical_move"),t.before)}_registerPhysicalTap(e,t,i){const s=performance.now(),n=this._physicalLastTap?.kind===e&&this._physicalLastTap.id===t&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:e,id:t,segment:i,at:s},n&&(this._physicalLastTap=null,this._openPhysicalDialog(e,t,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(e,t){if("square"!==t.shape||!this._spaceModel())return;e.preventDefault(),e.stopPropagation(),gv(e);const i=this._svgPoint(e);this._physicalSel={kind:"column",id:t.id},this._physicalRotate={pid:e.pointerId,id:t.id,center:[...t.center],startAngle:180*Math.atan2(i[1]-t.center[1],i[0]-t.center[0])/Math.PI,baseAngle:Zc(t.angle),angle:Zc(t.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(e){const t=this._physicalRotate;if(!t||t.pid!==e.pointerId)return;e.preventDefault(),e.stopPropagation();const i=this._svgPoint(e),s=180*Math.atan2(i[1]-t.center[1],i[0]-t.center[0])/Math.PI,n=t.baseAngle+s-t.startAngle,o=Zc(e.shiftKey?n:5*Math.round(n/5));this._physicalRotate={...t,angle:o,moved:t.moved||Math.abs(n-t.baseAngle)>=.5}}_physicalRotateUp(e){const t=this._physicalRotate;if(!t||t.pid!==e.pointerId)return;e.preventDefault(),e.stopPropagation(),this._physicalRotate=null;const i=this._spaceModel();if(!t.moved||!this._curSpaceCfg||!i)return;const s=i.wall_columns.find(e=>e.id===t.id);if(!s||"square"!==s.shape)return;const n={...s,angle:t.angle};if(i.wall_columns.some(e=>e.id!==t.id&&vh(e,n,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(e=>e.id===t.id);o&&(o.angle=t.angle,this._commitPhysicalGeometry(this._t("history.physical_edit"),t.before))}_rszRooms(){const e=[],t=this._spaceModel();if(!t)return e;for(const i of t.rooms){const t=i.id?hi(i):null;t&&e.push({id:i.id,poly:t,wall_ids:Array.isArray(i.wall_ids)?[...i.wall_ids]:void 0})}return i=e,s=Math.max(1e-12,1e-12*this._gridPitch),i.map(e=>{if(!Array.isArray(e.wall_ids)||e.wall_ids.length!==e.poly.length)return{...e,poly:e.poly.map(e=>[...e])};const t=e.poly.map(e=>[...e]);let i=!0;for(;i&&t.length>3;){i=!1;for(let e=0;e0){t.splice(e,1),i=!0;break}}}return{...e,poly:t}});var i,s}_rszOpenings(){return this._openingsR.map(e=>({id:e.id,x:e.rx,y:e.ry,length:e.rlen,hosted:!!e.host,angle:e.angle,type:e.type}))}_rszObstacles(){const e=this._spaceModel();if(!e)return[];const t=[];for(const i of e.partitions||[])t.push({kind:"segment",a:[...i.a],b:[...i.b],half:wl(i.cm,this._cellCm,this._gridPitch)/2});for(const i of e.room_drafts||[])for(let e=0;e+1Array.isArray(e.a)&&Array.isArray(e.b)&&e.a.length>=2&&e.b.length>=2).map(e=>({wall:e,a:[e.a[0]*lv,e.a[1]*this._spaceH],b:[e.b[0]*lv,e.b[1]*this._spaceH]})),n=Math.abs(e[0]-t[0])<=.05*this._gridPitch?"v":Math.abs(e[1]-t[1])<=.05*this._gridPitch?"h":null,o=s.filter(i=>!!n&&("h"===n?!(Math.abs(i.a[1]-e[1])>.05*this._gridPitch||Math.abs(i.b[1]-e[1])>.05*this._gridPitch)&&Math.min(Math.max(e[0],t[0]),Math.max(i.a[0],i.b[0]))-Math.max(Math.min(e[0],t[0]),Math.min(i.a[0],i.b[0]))>.05*this._gridPitch:!(Math.abs(i.a[0]-e[0])>.05*this._gridPitch||Math.abs(i.b[0]-e[0])>.05*this._gridPitch)&&Math.min(Math.max(e[1],t[1]),Math.max(i.a[1],i.b[1]))-Math.max(Math.min(e[1],t[1]),Math.min(i.a[1],i.b[1]))>.05*this._gridPitch)),a=new Set(o.map(e=>Number(e.wall.cm)).filter(e=>e>0));return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch,step:this._gridPitch,movingHalf:i>0?wl(i,this._cellCm,this._gridPitch)/2:0,obstacles:this._rszObstacles(),thicknessConflict:a.size>1}}_rszResolution(e,t,i){const s=this._resize.snapshotIdentity||i||this._rszSnapshot(),n=`${this._space}|${this._cellCm}|${this._gridPitch}|${s}`,o=`${e}:${t}`;return this._resize.resolve(n,o,()=>{const i=[...this._resize.rooms||this._rszRooms()],s=i.find(t=>t.id===e),n=s?.poly?.[t]||[0,0],o=s?.poly?.[(t+1)%(s?.poly?.length||1)]||[0,0];return function(e,t,i,s,n){const{eps:o}=n,a=e.find(e=>e.id===i);if(!a||s<0||s>=(a.poly?.length||0)||a.poly.length<4||!Ra(a.poly))return{enabled:!1,reason:"invalid-geometry"};const r=a.poly[s],l=a.poly[(s+1)%a.poly.length],c=Ea(r,l,o);if(!c)return{enabled:!1,reason:"diagonal"};if(!La(a.poly,s,c,o))return{enabled:!1,reason:"side-angle"};if(n.thicknessConflict)return{enabled:!1,reason:"thickness-conflict"};for(const e of n.obstacles||[])if(Wa(e,r,l,n))return{enabled:!1,reason:"duplicate-physical-wall"};const h=Da(Sa(l,r)),d=[],p=new Set;let u=!1,_=!1;for(const t of e)if(t.id!==i)for(let e=0;e1||d.length>1)return{enabled:!1,reason:"multiple-rooms"};const m={[i]:s},g=[i];if(1===d.length){const e=d[0];if(!Ra(e.room.poly))return{enabled:!1,reason:"invalid-geometry"};if(!La(e.room.poly,e.edge,c,o))return{enabled:!1,reason:"side-angle"};g.push(e.room.id),m[e.room.id]=e.edge}const f=[];for(const e of t)if(!(Ta([e.x,e.y],r,l)>2*o)){if(e.hosted||e.length>h+2*o)return{enabled:!1,reason:"opening-conflict"};f.push(e.id)}const v=Object.fromEntries(g.map(t=>[t,e.find(e=>e.id===t).poly.length])),y=[];for(const t of g){const i=e.find(e=>e.id===t),s=m[t],n=(s-1+i.poly.length)%i.poly.length,a=(s+1)%i.poly.length,r=Ga(e,t,n,1,o),l=Ga(e,t,a,0,o);if(!r||!l)return{enabled:!1,reason:"partial-shared"};y.push(r,l)}const b={roomId:i,edge:s,a:[...r],b:[...l],n:Ia(a.poly,s),roomIds:g,edgeByRoom:m,topology:v,movingOpeningIds:f,sideOwnership:y};if(!Qa(e,t,b,0,n))return{enabled:!1,reason:"invalid-geometry"};const w=Math.abs(Number(n.step));if(Number.isFinite(w)&&w>o){const i=[-w,w];if(!i.some(i=>Qa(e,t,b,i,n))){if(!i.some(i=>Ya(Ja(e,t,b,i),b,o)))return{enabled:!1,reason:"partial-shared"};const s={...n,obstacles:[]};return i.some(i=>Qa(e,t,b,i,s))?{enabled:!1,reason:"duplicate-physical-wall"}:t.length&&i.some(t=>Qa(e,[],b,t,n))?{enabled:!1,reason:"opening-conflict"}:{enabled:!1,reason:"invalid-geometry"}}}return{enabled:!0,plan:b}}(i,[...this._resize.openings||this._rszOpenings()],e,t,this._rszOptsFor(n,o))})}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszResetController(){const e=null!==this._resize.preview;this._resize.reset(),e&&this._cfgEpoch++}_rszProjectPreview(e,t,i,s,n){const o=this._serverCfg?.spaces.find(e=>e.id===this._space);if(!o||!this._serverCfg)return{ok:!1,reason:"missing-context"};const a=JSON.parse(e),r={...o,rooms:a.rooms,openings:a.openings||[]};for(const e of["walls","wall_segments","open_spans","room_drafts","partitions","wall_columns","decor"])void 0!==a[e]?r[e]=a[e]:delete r[e];for(const e of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==a.plan_transform?.[e]?r[e]=a.plan_transform[e]:delete r[e];const l=this._spaceH;for(const[e,i]of Object.entries(t)){const t=r.rooms.find(t=>t.id===e);t&&(t.poly=i.map(e=>[e[0]/lv,e[1]/l]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[e,t]of Object.entries(i)){const i=(r.openings||[]).find(t=>t.id===e);i&&(i.x=t[0]/lv,i.y=t[1]/l)}const c=[],h=[];for(const e of s){const t=n.find(t=>t.id===e),i=r.rooms.find(t=>t.id===e);if(!t||!i?.poly)continue;const s=i.poly.map(e=>[e[0]*lv,e[1]*l]);if(t.poly.length!==s.length)continue;const o=Array.isArray(t.wall_ids)?t.wall_ids:[];for(let e=0;ee.id===i):null;n&&(n.a=[s[e][0]/lv,s[e][1]/lv],n.b=[s[(e+1)%s.length][0]/lv,s[(e+1)%s.length][1]/lv])}}if(c.length&&Array.isArray(r.walls)&&r.walls.length){const e=function(e,t,i,s,n=1,o="fixed-topology"){let a=!1;const r=Nl(e,t,i,s,n,o,()=>{a=!0});return{walls:a?(e||[]).map(e=>({...e})):r,rejected:a}}(r.walls,c,h,this._wallKeyPitch,lv,"fixed-topology");if(e.rejected)return{ok:!1,reason:"wall-metadata"};r.walls=e.walls}const d=[];for(const e of r.rooms||[]){const t=hi(e);if(t&&!(t.length<2))for(let e=0;eJSON.stringify([e?.key,e?.cm,e?.a,e?.b]),u=new Map;for(const e of a.walls||[]){const t=p(e);u.set(t,(u.get(t)||0)+1)}const _=[];for(const e of r.walls||[]){const t=p(e),i=u.get(t)||0;i?u.set(t,i-1):_.push(e)}if(Hl(_,d,this._wallKeyPitch,lv,a.walls||[]).length)return{ok:!1,reason:"wall-metadata"};const m=this._rszSpaceCandidateGeometry(this._space,r);return m.ok?{ok:!0,value:{preview:{space:this._space,sp:r},beforeWalls:a.walls||[],afterWalls:r.walls||[],artifact:m.wallGeometry}}:{ok:!1,reason:"physical-geometry"}}_rszAcceptPreview(e,t){if(this._cfgEpoch++,!e||!t)return;const i=Oc(t);if(!i)return;const s=`${this._space}|${this._cfgEpoch}|${e.sp.rooms.length}`;Object.defineProperty(i,"sourceFingerprint",{value:Md([e.sp,this._cellCm,this._gridPitch]),enumerable:!1});const n={key:s,value:i};Jf(this._wallUnionPool,s,n,8),this._wallUnionCache=n}_rszSpaceCandidateGeometry(e,t){if(!this._serverCfg)return{ok:!1,wallGeometry:null};const i={...this._serverCfg,spaces:this._serverCfg.spaces.map(i=>i.id===e?t:i)};let s=null;try{return{ok:this._checkSpacePhysicalGeometry(i,e,e=>{s=e}).ok,wallGeometry:s}}catch{return{ok:!1,wallGeometry:null}}}_rszSpaceCandidateRenderable(e,t){if(!this._serverCfg)return!1;try{const i={...this._serverCfg,spaces:this._serverCfg.spaces.map(i=>i.id===e?t:i)};return this._checkSpacePhysicalGeometry(i,e).ok}catch{return!1}}_rszCandidateRenderable(e){return!!e&&e.space===this._space&&this._rszSpaceCandidateRenderable(e.space,e.sp)}_rszEdgeDown(e,t,i){if("resize"!==this._tool||this._resize.dragging)return;e.stopPropagation(),e.preventDefault();const s=this._rszRooms(),n=this._rszResolution(t,i);if(!n.enabled)return void this._showToast(this._rszReasonText(n.reason));gv(e);const o=n.plan,a=this._svgPoint(e),r=`${this._space}|${this._cfgEpoch}|${s.length}`,l=this._wallUnionCache?.key===r?this._wallUnionCache.value:null,c=this._rszSnapshot();this._resize.begin({pointerId:e.pointerId,start:[a[0],a[1]],roomId:t,plan:o,options:this._rszOptsFor(o.a,o.b),rooms:s,openings:this._rszOpenings(),snapshotIdentity:c,before:JSON.parse(c),wallUnionBefore:l})}_rszReasonText(e){return this._t(`resize.disabled.${e}`)}_rszDisabledActivate(e,t){e.stopPropagation(),e.preventDefault(),this._showToast(this._rszReasonText(t))}_rszDisabledKey(e,t){"Enter"!==e.key&&" "!==e.key||this._rszDisabledActivate(e,t)}_rszMove(e){if(!this._resize.ownsPointer(e.pointerId))return;e.stopPropagation();const t=this._svgPoint(e),i=this._resize.move({pointerId:e.pointerId,point:[t[0],t[1]],step:this._gridPitch,snap:e=>{const t=this._snap(e);return[t[0],t[1]]},project:(e,t,i,s,n)=>this._rszProjectPreview(e,t,i,s,n),publish:(e,t)=>this._rszAcceptPreview(e,t),measure:(e,t)=>this._rszEdgeLabels(e,t)});if("rejected"===i.kind)return i.notify&&this._showToast(this._t("resize.preview_failed")),void this.requestUpdate();"accepted"===i.kind&&this.requestUpdate()}_rszUp(e){if(!this._resize.ownsPointer(e.pointerId))return;e.stopPropagation();const t=this._resize.finish({pointerId:e.pointerId,currentSnapshotIdentity:this._rszSnapshot(),validatePreview:e=>this._rszCandidateRenderable(e)});if("no-op"===t.kind)return this._cfgEpoch++,void this.requestUpdate();if("rejected"===t.kind)return this._cfgEpoch++,this._showToast(this._t("resize.commit_failed")),void this.requestUpdate();const i=t.preview,s=this._serverCfg?.spaces.find(e=>e.id===i.space);s&&(s.rooms=i.sp.rooms,s.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?s.walls=i.sp.walls:delete s.walls),Array.isArray(i.sp.wall_segments)&&(s.wall_segments=i.sp.wall_segments)),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._commitPhysicalGeometry(this._t("history.resize_room"),t.before),this.requestUpdate()}_rszCancelDrag(e){const t=this._resize.cancel(this._rszSnapshot(),e);if("no-op"!==t.kind){if(this._cfgEpoch++,t.restoreWallUnion){const e=this._spaceModel();if(e){const i=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`,s={key:i,value:t.restoreWallUnion};Jf(this._wallUnionPool,i,s,8),this._wallUnionCache=s}}this.requestUpdate()}}_rszPointerCancel(e){this._resize.ownsPointer(e.pointerId)&&(e.stopPropagation(),this._rszCancelDrag(e.pointerId))}_rszEdgeLabels(e,t,i=this._resize.rooms){const s=i,n=[],o=e.polys[t.roomId]||s?.find(e=>e.id===t.roomId).poly;if(!o||!s)return n;const a=o.length,r=this._rszInnerSpanCms(t.roomId,o,e.polys);for(const e of function(e,t){const i=e.length;return[(t-1+i)%i,(t+1)%i]}(o,t.edge)){const t=o[e],i=o[(e+1)%a],s=r?.[e];n.push({kind:"length",x:(t[0]+i[0])/2,y:(t[1]+i[1])/2,text:null==s?this._fmtLen(t,i):li(s,"mi"===this.hass?.config?.unit_system?.length),edge:{a:[t[0],t[1]],b:[i[0],i[1]]}})}const l="mi"===this.hass?.config?.unit_system?.length,c=t.roomIds,h=this._spaceWalls,d=this._physicalBodiesR(),p=this._baseVb(),u=this._view&&this._view.w>0&&this._view.h>0?this._view:{x:p[0],y:p[1],w:p[2],h:p[3]},[_,m]=this._lastValidStageSize||[u.w,u.h],g={...u,stageWidth:Math.max(1,_),stageHeight:Math.max(1,m)},f=this._spaceModel(),v=this._config?.icon_size??2.5,y=f?Zh(v>8?2.5:v,f,u.w,this._kiosk?this._kioskScale.icon:1)*g.stageWidth/100:24,b=Math.max(10,.77*y),w=this._t("room.settings_short"),k=b*(1.457+.66*Math.max(1,w.length)*.42);for(const i of c){const o=e.polys[i]||s.find(e=>e.id===i).poly,a=h.length&&f&&this._innerRoomContour(f,i)||o,r=ir(d.length?uh(ph(a,d))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:tr(a,this._gridPitch,this._cellCm),l),c=cr({poly:o,edge:t.edgeByRoom[i],text:r,view:g,gearCenter:$i(o),gearWidthPx:k,gearHeightPx:b});n.push({kind:"area",roomId:i,x:c.anchor[0],y:c.anchor[1],text:r,placement:c})}return n}_rszInnerSpanCms(e,t,i){const s=Object.keys(i).length?Object.entries(i).map(([e,t])=>({id:e,poly:t})):this._spaceModel()?.rooms;if(!s?.length)return null;const n=this._spaceWalls;if(!n.length)return null;const o=function(e,t,i,s,n,o,a,r=1){const l=(e||[]).find(e=>e?.id===t),c=hi(l);if(!c||c.length<3)return null;const h=mc(e,t,i,s,n,o,a,r);if(!h)return c.map(()=>0);const d=4*Zl(n,r);return c.map((e,t)=>{const i=c[(t+1)%c.length],s=[(e[0]+i[0])/2,(e[1]+i[1])/2];for(let e=0;efunction(e,t,i){const s=e?.length||0;if(s<3||!Array.isArray(i)||i.length!==s)return 0;const n=(Math.trunc(t)%s+s)%s,o=e[n],a=e[(n+1)%s],r=a[0]-o[0],l=a[1]-o[1],c=Math.hypot(r,l);if(!(c>0))return 0;const h=Math.max(0,Number(i[n])||0);if(!(h>0))return c;const d=[r/c,l/c],p=ec(e,n),u=[o[0]+p[0]*h,o[1]+p[1]*h],_=t=>{const n=Math.max(0,Number(i[t])||0);if(!(n>0))return null;const a=e[t],r=e[(t+1)%s],l=r[0]-a[0],c=r[1]-a[1],h=Math.hypot(l,c);if(!(h>0))return null;const p=ec(e,t),_=ic(u,d,[a[0]+p[0]*n,a[1]+p[1]*n],[l/h,c/h]);return _?(_[0]-o[0])*d[0]+(_[1]-o[1])*d[1]:null},m=_((n-1+s)%s)??0,g=(_((n+1)%s)??c)-m;return g>0?g:0}(t,i,o)*a)}_renderResizeMeasurements(){const e=this._resize.liveLabels;if(!e?.length)return G;const t=e.filter(e=>"length"===e.kind),i=e.filter(e=>"area"===e.kind);return j``}_zeroWalls(){const e=this._spaceModel();return e?k_(this._curSpaceCfg,e,cv,.02*this._gridPitch):{style:v_(this._curSpaceCfg),lines:[],contour:[],barriers:[],transmissive:[]}}_openCuts(){return this._zeroWalls().contour}_deleteRoomClick(e){const t=this._spaceModel();if(!t)return;const i=[...t.rooms].reverse().find(t=>this._pointInRoom(e,t));i?i.id&&(this._roomDeleteDialog={roomId:i.id,name:i.name}):this._showToast(this._t("toast.delete_room_pick"))}get _wallKeyPitch(){return Fh}get _spaceWalls(){const e=this._curSpaceCfg?.walls;return Array.isArray(e)?e:[]}_cfgOpenCuts(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return[];const i=y_(e,1),s=w_(e,t.rooms,cv,.02*this._gridPitch).map(e=>e.map(e=>e/cv)),n=new Set;return[...i,...s].filter(e=>{const t=e.map(e=>Number(e).toFixed(9)).join(","),i=[e[2],e[3],e[0],e[1]].map(e=>Number(e).toFixed(9)).join(","),s=t[e[0]/cv,e[1]/cv,e[2]/cv,e[3]/cv]))}_paperShapes(e){if(!this._spaceWalls.length)return di(e);const t=this._wallUnionGeometry();return t?.paperD?[{path:t.paperD}]:di(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 s=`${this._space}|${this._cfgEpoch}|${e.rooms.length}`;if(!this._wallUnionCache||this._wallUnionCache.key!==s){const n=Jf(this._wallUnionPool,s);if(n.hit)this._wallUnionCache=n.value;else{const n=this._openCuts(),o=this._roomWallOpeningInputs(),a=Nc(e.rooms,t,n,o,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,i);a&&Object.defineProperty(a,"sourceFingerprint",{value:Md([this._curSpaceCfg,this._cellCm,this._gridPitch]),enumerable:!1});const r={key:s,value:a};Xf(this._wallUnionPool,s,r,8),this._wallUnionCache=r}}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 Lc(e.rooms,t,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv).map(e=>[e.a[0],e.a[1],e.b[0],e.b[1]])}_innerRoomContour(e,t,i=this._openCuts(),s=this._wallUnionGeometry()?.roomGeom,n=this._wallUnionGeometry()?.multiWallNodes){const o=i.map(e=>e.join(",")).join(";"),a=`${e.id}|${this._cfgEpoch}|${t}|${o}`,r=Jf(this._innerContourCache,a);if(r.hit)return r.value;const l=kc(e.rooms,t,this._spaceWalls,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,s,n);return Xf(this._innerContourCache,a,l,600),l}_wallThickHit(e){const t=this._spaceModel();if(!t)return null;const i=6*this._gridPitch,s=this._openCuts();let n=null;const o=(e,t,s)=>{t>i||(!n||t0?e:0,n=Number.isFinite(t)&&t>0?t:5,o=Number.isFinite(i)&&i>0?i:0;return s>0?wl(s,n,o)/2:wh(1.5*o,n)}(e.cm,this._cellCm,this._gridPitch);let i="";for(const s of e.segs)i+=(i?" ":"")+Jl([[s[0],s[1]],[s[2],s[3]]],t,!1);return{segs:e.segs,open:e.open,d:i}}_wallThickClick(e){const t=this._wallThickHit(e);if(!t)return void this._showToast(this._t("toast.wallthick_pick"));const i=t.cm,s=this._viewOr(this._baseVb()),n=(t.a[0]+t.b[0])/2,o=(t.a[1]+t.b[1])/2;this._wallDialog={a:t.a,b:t.b,value:bl(i,this._imperial),roomId:t.roomId,source:t.source,sx:(n-s.x)/s.w*100,sy:(o-s.y)/s.h*100}}_wallThickApply(e){const t=this._wallDialog;if(!t)return;const i=this._curSpaceCfg,s=this._spaceModel();if(!i||!s)return;const n=t.value.trim(),o=n?Yf(n):null;if(null==o)return void this._showPhysicalRange(100);const a=this._imperial?2.54*o:o;if(!Number.isFinite(a)||a<0||a>100)return void this._showPhysicalRange(100);if("room"!==t.source.kind){const e=this._geometrySnapshot();if("partition"===t.source.kind){const e=(i.partitions||[]).find(e=>e.id===t.source.id);if(!e)return;if(0===a&&x_(i.openings,{kind:"partition",id:e.id}))return void this._showToast(this._t("toast.zero_wall_opening_conflict"));e.cm=a}else{const e=(i.room_drafts||[]).find(e=>e.id===t.source.id),s=e?.segments?.[t.source.segment];if(!s)return;s.cm=a}this._wallDialog=null;return this._commitPhysicalGeometry(this._t("history.wall_thickness"),e)&&this._showToast(this._t("toast.wallthick_set")),void this.requestUpdate()}const r=this._geometrySnapshot(),l=a>0?a:null;if(0===a){const s=.02*this._gridPitch,n=[t.a[0],t.a[1],t.b[0],t.b[1]],o=(i.rooms||[]).find(e=>e.id===t.roomId),a=new Set(e&&o&&Array.isArray(o.wall_ids)?o.wall_ids:(i.wall_segments||[]).filter(e=>{const t=[Number(e.a?.[0])*cv,Number(e.a?.[1])*cv],i=[Number(e.b?.[0])*cv,Number(e.b?.[1])*cv];return Ls(t,n)<=s&&Ls(i,n)<=s}).map(e=>String(e.id))),r=(i.openings||[]).some(e=>{if("partition"===e.host?.kind)return!1;if("wall"===e.host?.kind&&a.has(e.host.id))return!0;if(e.host)return!1;const t=[Number(e.x)*cv,Number(e.y)*cv];return t.every(Number.isFinite)&&Ls(t,n)<=s});if(r)return void this._showToast(this._t("toast.zero_wall_opening_conflict"))}let c,h=this._openCuts();if(a>0&&h.length){const n=[];if(e&&t.roomId){const e=(i.rooms||[]).find(e=>e.id===t.roomId),o=new Set(Array.isArray(e?.wall_ids)?e.wall_ids:[]);for(const e of i.wall_segments||[])o.has(e.id)&&n.push([Number(e.a?.[0])*cv,Number(e.a?.[1])*cv,Number(e.b?.[0])*cv,Number(e.b?.[1])*cv]);if(!n.length){const e=s.rooms.find(e=>e.id===t.roomId),i=e?hi(e):null;for(let e=0;i&&e!n.some(t=>Ls([e[0],e[1]],t)<=o&&Ls([e[2],e[3]],t)<=o&&Ls([t[0],t[1]],e)<=o&&Ls([t[2],t[3]],e)<=o))}c=e&&t.roomId?Bl(i.walls,s.rooms,t.roomId,l,this._wallKeyPitch,h,cv):Hl(i.walls,t.a,t.b,l,this._wallKeyPitch,cv),c=this._normalizeWalls(c,h);for(const e of i.wall_segments||[])e.cm=zl(c,e.a,e.b,Fh,1);c.length?i.walls=c:delete i.walls,this._wallDialog=null,this._commitPhysicalGeometry(this._t("history.wall_thickness"),r)&&this._showToast(this._t(0===a?"toast.wallthick_cleared":"toast.wallthick_set")),this.requestUpdate()}_wallHatchDefs(e){if(!this._spaceWalls.length&&!this._physicalBodiesR().length&&!this._markup)return j``;const t=fl(this._cellCm);return j` - `}_resolvedRoomFills(e,t){const i=new Map,s=new Map;for(const n of e.rooms){const e=this._roomDialog&&n.id===this._roomEditId?this._roomFill||t.fill:zs(t.fill,n),o=this._roomDialog&&n.id===this._roomEditId?this._roomCustomFill||t.customFill:ls(t.customFill,n),a=_s(e,"lqi"===e&&n.area?this._roomLqi(n.area):null,"light"===e?uo(ho(this._renderPlanHass,this._renderDevices,n,this._virtualLights)):"none","temp"===e?this._roomTemp(n):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(n,a),n.id&&s.set(n.id,a)}return{byRoom:i,byId:s}}_spaceDisplayForRender(){const e=cs(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?rs(t.customFill):as,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(";"),s=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),n=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,s,n].join("|");let a=this._openingWallIndexCache.get(o);return a||(a=Bc(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,lv)),Jf(this._openingWallIndexCache,o,a,4),{key:o,value:a}}_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 s=this._openCuts(),n=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=n.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),a=this._openingWallIndexFor(e,s),r=`${a.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===r||(this._openingTunnelCache={key:r,value:Kc(a.value,n)}),p_({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 s=new Map,n=new Map,o=this._fillColors.glow_base;for(const a of e.rooms){const e=i.byRoom.get(a),r=Fs(t.glow,a)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;s.set(a,r),a.id&&n.set(a.id,r)}return{byRoom:s,byId:n}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,hi(e)])),s=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",a=e.rooms.map(a=>{const r=t.byRoom.get(a)||null,l=i.get(a)||null;if(!r||!l)return G;const c=this._spaceWalls.length&&a.id&&this._innerRoomContour(e,a.id,s,n)||l,h=Pi(c,e.rooms.filter(e=>e!==a).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(a,c,e).path;return d||h.length?j``}_resolvedRoomFills(e,t){const i=new Map,s=new Map;for(const n of e.rooms){const e=this._roomDialog&&n.id===this._roomEditId?this._roomFill||t.fill:zs(t.fill,n),o=this._roomDialog&&n.id===this._roomEditId?this._roomCustomFill||t.customFill:ls(t.customFill,n),a=_s(e,"lqi"===e&&n.area?this._roomLqi(n.area):null,"light"===e?uo(ho(this._renderPlanHass,this._renderDevices,n,this._virtualLights)):"none","temp"===e?this._roomTemp(n):null,t.tempMin,t.tempMax,this._fillColors,o);i.set(n,a),n.id&&s.set(n.id,a)}return{byRoom:i,byId:s}}_spaceDisplayForRender(){const e=cs(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?rs(t.customFill):as,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(";"),s=this._spaceWalls.map(e=>`${e.key}:${e.a?.join(",")||""}:${e.b?.join(",")||""}:${e.cm}`).join(";"),n=t.map(e=>e.join(",")).join(";"),o=[e.id,this._cfgEpoch,this._wallKeyPitch,this._cellCm,this._gridPitch,i,s,n].join("|");let a=this._openingWallIndexCache.get(o);return a||(a=Bc(e.rooms,this._spaceWalls,t,this._wallKeyPitch,this._cellCm,this._gridPitch,cv)),Xf(this._openingWallIndexCache,o,a,4),{key:o,value:a}}_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 s=this._openCuts(),n=this._openingsR.map(e=>({x:e.rx,y:e.ry,angle:e.angle,length:e.rlen})),o=n.map(e=>`${e.x},${e.y},${e.angle},${e.length}`).join(";"),a=this._openingWallIndexFor(e,s),r=`${a.key}|${o}`;return this._openingTunnelCache&&this._openingTunnelCache.key===r||(this._openingTunnelCache={key:r,value:Kc(a.value,n)}),u_({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 s=new Map,n=new Map,o=this._fillColors.glow_base;for(const a of e.rooms){const e=i.byRoom.get(a),r=Fs(t.glow,a)&&(!e||e.opacity<=0)?{color:o.c,opacity:o.a,mode:"glow"}:null;s.set(a,r),a.id&&n.set(a.id,r)}return{byRoom:s,byId:n}}_renderGlowBaseRooms(e,t){if(this._markup||![...t.byRoom.values()].some(Boolean))return j``;const i=new Map(e.rooms.map(e=>[e,hi(e)])),s=this._openCuts(),n=this._wallUnionGeometry()?.roomGeom,o=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",a=e.rooms.map(a=>{const r=t.byRoom.get(a)||null,l=i.get(a)||null;if(!r||!l)return G;const c=this._spaceWalls.length&&a.id&&this._innerRoomContour(e,a.id,s,n)||l,h=Pi(c,e.rooms.filter(e=>e!==a).map(e=>i.get(e)).filter(e=>!!e)),d=this._cleanFloor(a,c,e).path;return d||h.length?j``:a.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?vf(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=bh(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?yf(e,this._openingAmt(t)):[]}).sort((e,t)=>e.depth-t.depth||e.sourceIndex-t.sourceIndex||e.leaf-t.leaf)}_renderIsoDefs(e,t){const i=bh(this._cellCm);return j` ${"walls"===t&&e.materialNuance?j` @@ -5385,7 +5385,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${e.panels?j``:G} - `: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 s=hi(i);if(!s)return null;const n=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:hi(e)})).filter(e=>!!e.poly),o=Pi(s,n.map(e=>e.poly)),a=this._openCuts(),r=.02*this._gridPitch,l=a.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return s.some((e,i)=>Hs(t,[e[0],e[1],...s[(i+1)%s.length]])<=4*r)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,a,h)||s,p=[{axis:s,face:d}];for(const t of o){const i=n.find(e=>e.poly===t)?.room;let s=t;if(c.length&&i?.id){const n=mc(e.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,lv);n&&(s=Lc(n.poly,n.offsets)||t)}p.push({axis:t,face:s})}const u=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;return[e.x-i,e.y-s,e.x+i,e.y+s]}),_=l.concat(u),m=p.map(({axis:e,face:t})=>{const i=_.map(i=>((e,t,i)=>{const s=e[2]-e[0],n=e[3]-e[1],o=Math.hypot(s,n);if(o.05)&&Hs([c,h],[i[0],i[1],s[0],s[1]])<=4*r){d=!0;break}}if(!d)return null;let p=null;for(let e=0;e.05)continue;const u=Hs([c,h],[t[0],t[1],s[0],s[1]]);(!p||u!!e);return i.length?Ps(t,i,r).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(!m)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:m}}_renderRoomHoverFill(e){return e?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 s=hi(i);if(!s)return null;const n=e.rooms.filter(e=>e!==i).map(e=>({room:e,poly:hi(e)})).filter(e=>!!e.poly),o=Pi(s,n.map(e=>e.poly)),a=this._openCuts(),r=.02*this._gridPitch,l=a.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return s.some((e,i)=>Ls(t,[e[0],e[1],...s[(i+1)%s.length]])<=4*r)}),c=this._spaceWalls,h=this._wallUnionGeometry()?.roomGeom,d=c.length&&i.id&&this._innerRoomContour(e,i.id,a,h)||s,p=[{axis:s,face:d}];for(const t of o){const i=n.find(e=>e.poly===t)?.room;let s=t;if(c.length&&i?.id){const n=mc(e.rooms,i.id,c,a,this._wallKeyPitch,this._cellCm,this._gridPitch,cv);n&&(s=Hc(n.poly,n.offsets)||t)}p.push({axis:t,face:s})}const u=this._roomWallOpeningInputs(this._openingsR,e).map(e=>{const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;return[e.x-i,e.y-s,e.x+i,e.y+s]}),_=l.concat(u),m=p.map(({axis:e,face:t})=>{const i=_.map(i=>((e,t,i)=>{const s=e[2]-e[0],n=e[3]-e[1],o=Math.hypot(s,n);if(o.05)&&Ls([c,h],[i[0],i[1],s[0],s[1]])<=4*r){d=!0;break}}if(!d)return null;let p=null;for(let e=0;e.05)continue;const u=Ls([c,h],[t[0],t[1],s[0],s[1]]);(!p||u!!e);return i.length?Ps(t,i,r).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(!m)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:m}}_renderRoomHoverFill(e){return e?j` `:j``}_renderRoomHoverOutline(e){return e?j` @@ -5410,7 +5410,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922
-
`:W``}_openingAt(e){if(!this._openingsR.length)return null;const t=this._openingsR.flatMap(t=>{const i=t.angle*Math.PI/180,s=e[0]-t.rx,n=e[1]-t.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>t.rlen/2+wh(12,this._cellCm)?[]:[{o:t,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!t.length)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return t.find(({o:e,localY:t})=>{const i="gate"===e.type?!e.flip_v:e.flip_v,s=e.partitionHost||this._spaceWalls.length||"gate"===e.type?this._openingFace(e,n,!!i):{ox:0,oy:0,cm:0,side:-1},o=Uu({type:e.type,length:e.rlen,angle:e.angle,amount:this._openingAmt(e),flipH:!!e.flip_h,flipV:!!e.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s});return Math.abs(t)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(e){const t=this._openingPreset;if("opening"!==this._tool||!t)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s),o=`${n.key}|partitions:${this._cfgEpoch}`;this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===o||(this._openingPlacementIntervalsCache={key:o,value:[...gc(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,lv),...Fp(i.partitions,this._cellCm,this._gridPitch)]}),this._openingDimensionContextCache&&this._openingDimensionContextCache.key===o||(this._openingDimensionContextCache={key:o,value:Af({rooms:i.rooms,walls:this._spaceWalls,openCuts:s,partitions:i.partitions,roomOpenings:this._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(i),pitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:lv,epsilon:2e-4*this._gridPitch})});const a=$f({pointer:[e[0],e[1]],preset:t,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(t.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this._pointerModality.modality||"pen"===this._pointerModality.modality?10:6),gridStep:this._gridPitch});this._openingJambBlockCm=a.jambBlockedTarget?a.jambBlockedTarget.physicalHalfWidth/this._gridPitch*this._cellCm:null;const r=a.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let c;if(r.host){const e=Tp({id:"preview",type:r.type,x:r.x/lv,y:r.y/lv,angle:r.angle,length:r.renderedLength/lv,host:r.host},i.partitions,lv,this._cellCm,this._gridPitch).resolved;c=e?zp(e,l):{ox:0,oy:0,cm:0,side:-1}}else c=Uc(n.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const h="mi"===this.hass?.config?.unit_system?.length,d=Lf(r,this._openingDimensionContextCache.value).map(e=>({x:e.label[0],y:e.label[1],text:li(e.distance/this._gridPitch*this._cellCm,h),dimension:e}));return{...r,face:c,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(e){this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingPreset=function(e,t){return{type:e,lengthCm:bf(e),flipH:!1,flipV:!1,revision:t}}(e,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null)}_clearOpeningPlacement(e){this._openingHoverCandidate=null,this._openingJambBlockCm=null,e&&(this._openingPreset=null,this._openingRebindId=null)}_openingClick(e){const t=this._spaceModel();if(!t)return;const i=this._openingRebindId?null:this._openingAt(e);if(i)return void this._editOpening(i);const s=this._openingPreset;if(!s)return;const n=this._openingHoverCandidate,o=n&&Sf(n,[e[0],e[1]],s.revision,this._cfgEpoch)?n:this._resolveOpeningPlacement(e);if(!o){if(null!=this._openingJambBlockCm)return void this._showToast(this._t("opening.partition_jamb_margin",{distance:li(this._openingJambBlockCm,this._imperial)}));const i=1.5*this._gridPitch,s=ui(e,t.rooms,i);return s&&function(e,t,i,s,n){for(const o of s)if(!(Hs([e,t],o)>n)&&El([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),i)?void this._showToast(this._t("toast.opening_on_zero_wall")):void this._showToast(this._t("toast.opening_no_wall"))}const a=this._openingRebindId?this._curSpaceCfg?.openings?.find(e=>e.id===this._openingRebindId):null;this._openingDialog={...a?{id:a.id,type:a.type,lengthCm:Math.round(a.length*lv/this._gridPitch*this._cellCm),lengthTouched:!1,contact:a.contact||"",lock:a.lock||"",invert:!!a.invert,flipH:!!a.flip_h,flipV:!!a.flip_v}:{type:o.type,lengthCm:o.lengthCm,contact:"",lock:"",invert:!1,flipH:o.flipH,flipV:o.flipV},...o.host?{host:o.host}:{},x:o.x,y:o.y,angle:o.angle},this._openingRebindId=null,this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(e){this._openingDialog={id:e.id,type:e.type,lengthCm:Math.round(e.rlen/this._gridPitch*this._cellCm),lengthTouched:!1,contact:e.contact||"",lock:e.lock||"",invert:!!e.invert,flipH:!!e.flip_h,flipV:!!e.flip_v,..."partition"===e.host?.kind?{host:{...e.host}}:{},x:e.rx,y:e.ry,angle:e.angle}}_opPointerDown(e,t){if("plan"===this._mode&&this._spaceModel()&&"resize"!==this._tool){e.preventDefault(),e.stopPropagation();try{gv(e)}catch{}this._opDrag={id:t.id,moved:!1,sx:e.clientX,sy:e.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;if(Math.abs(e.clientX-this._opDrag.sx)+Math.abs(e.clientY-this._opDrag.sy)<=3)return;const i=this._spaceModel();if(!i)return;const s=this._svgPoint(e),n=this._curSpaceCfg,o=n?.openings?.find(e=>e.id===t.id);if(!o)return;if("partition"===o.host?.kind){const e=i.partitions.find(e=>e.id===o.host.id);if(!e)return;const t=e.b[0]-e.a[0],n=e.b[1]-e.a[1],a=Math.hypot(t,n);if(!(a>1e-9))return;const r=t/a,l=n/a;if(Math.abs((s[0]-e.a[0])*l-(s[1]-e.a[1])*r)>4*this._gridPitch)return;const c=o.length*lv/2+Dp(e,this._cellCm,this._gridPitch);let h=(s[0]-e.a[0])*r+(s[1]-e.a[1])*l;if(h=Math.round(h/this._gridPitch)*this._gridPitch,h=Math.max(c,Math.min(a-c,h)),a<2*c-1e-9)return;const d=h/a,p=(e.a[0]+r*h)/lv,u=(e.a[1]+l*h)/this._spaceH;let _=180*Math.atan2(n,t)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=o.host.t!==d||o.x!==p||o.y!==u||o.angle!==_;return this._opDrag.moved=!0,m&&(this._opDrag.dirty=!0),o.host={...o.host,t:d},o.x=p,o.y=u,o.angle=_,this._opMeasure=null,m&&this._cfgEpoch++,void this.requestUpdate()}const a=ui(s,i.rooms,4*this._gridPitch);if(!a)return;this._opDrag.moved=!0;const r=this._opRuler(a,o.length*lv);this._opMeasure=r.measure;const l=r.x/lv,c=r.y/this._spaceH,h=o.x!==l||o.y!==c||o.angle!==a.angle;if(h&&(this._opDrag.dirty=!0),o.x=l,o.y=c,o.angle=a.angle,Number(this._serverCfg?.model_version||0)>=9){const e=eg(o,n?.wall_segments||[]);e?o.host=e:delete o.host}h&&this._cfgEpoch++,this.requestUpdate()}_opRuler(e,t){const i=this._spaceModel()?.rooms||[],s=this._gridPitch/2;let n=e.x,o=e.y,a=_i([n,o],e.angle,t,i,s);if(a&&a.centered&&(n!==a.wallCenter[0]||o!==a.wallCenter[1]))[n,o]=a.wallCenter,a=_i([n,o],e.angle,t,i,s);else if(a){const[r,l]=a.wallA,[c,h]=a.wallB,d=c-r,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(t/2,u/2);let _=Math.round(((n-r)*d+(o-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),n=r+_/u*d,o=l+_/u*p,a=_i([n,o],e.angle,t,i,s)||a}}if(!a)return{x:n,y:o,angle:e.angle,measure:null};const r="mi"===this.hass?.config?.unit_system?.length,l=(e,t)=>({x:t[0],y:t[1],text:li(e/this._gridPitch*this._cellCm,r)});return{x:n,y:o,angle:e.angle,measure:{labels:[l(a.sideA,a.midA),l(a.sideB,a.midB)],guide:a.centered?{x:a.wallCenter[0],y:a.wallCenter[1],angle:e.angle}:null}}}_opPointerUp(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&this._commitPhysicalGeometry(this._t("history.move_opening"),i.before),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(e,t){"plan"===this._mode&&"resize"===this._tool||(e.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(t))}_saveOpening(){const e=this._openingDialog,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=this._geometrySnapshot(),n=this._spaceH,o=(t.openings||[]).find(t=>t.id===e.id),a={...o||{},id:e.id||"o"+Date.now().toString(36),type:e.type,x:e.x/lv,y:e.y/n,angle:e.angle,length:o&&!e.lengthTouched?o.length:this._cmToUnits(Math.max(20,e.lengthCm))/lv,...e.host?{host:{...e.host}}:{}};if("partition"===a.host?.kind){const e=Rp(o,a)?Ip(a,i.partitions,lv,this._cellCm,this._gridPitch):Tp(a,i.partitions,lv,this._cellCm,this._gridPitch);if(!e.resolved){const t=i.partitions.find(e=>e.id===a.host.id);if("does-not-fit-jamb"===e.reason&&t){const e=Dp(t,this._cellCm,this._gridPitch);this._showToast(this._t("opening.partition_jamb_margin",{distance:li(e/this._gridPitch*this._cellCm,this._imperial)}))}else this._showToast(this._t("opening.partition_orphan"));return}const s=(t.openings||[]).flatMap(e=>{if(!e.host||e.id===a.id)return[];const t=Tp(e,i.partitions,lv,this._cellCm,this._gridPitch).resolved;return t?[t]:[]});if(function(e,t,i=1e-9){const s=e.length/(2*e.axis.length),n=e.t-s,o=e.t+s;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const s=t.length/(2*t.axis.length);return Math.max(n,t.t-s)=9){const e=eg(a,t.wall_segments||[]);if(!e)return void this._showToast(this._t("toast.opening_no_wall"));a.host=e}else delete a.host;"passage"===e.type?(delete a.contact,delete a.lock,delete a.invert,delete a.flip_h,delete a.flip_v):(a.contact=e.contact||null,a.lock=("door"===e.type||"gate"===e.type)&&e.lock||null,a.invert=e.invert||void 0,a.flip_h="gate"!==e.type&&e.flipH||void 0,a.flip_v=e.flipV||void 0),t.openings=t.openings||[];const r=t.openings.findIndex(e=>e.id===a.id);r>=0?t.openings[r]=a:t.openings.push(a),this._commitPhysicalGeometry(this._t(e.id?"history.edit_opening":"history.add_opening"),s),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const e=this._openingDialog,t=this._curSpaceCfg;if(!e?.id||!t?.openings)return;const i=this._geometrySnapshot();t.openings=t.openings.filter(t=>t.id!==e.id),this._commitPhysicalGeometry(this._t("history.delete_opening"),i),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const e=[];for(const t of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(t))continue;const i=t.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[t],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&e.push([t,s?.attributes?.friendly_name||t,n?0:1])}return e.sort((e,t)=>e[2]-t[2]||e[1].localeCompare(t[1])).map(([e,t])=>({value:e,label:t}))}_lockCandidates(){return Object.keys(this.hass.states).filter(e=>e.startsWith("lock.")&&this._openingEntityAvailable(e)).map(e=>({value:e,label:this.hass.states[e]?.attributes?.friendly_name||e})).sort((e,t)=>e.label.localeCompare(t.label))}_toggleOpeningEntityPicker(e){const t=this._openingDialog;if(!t)return;const i="contact"===e?!t.contactOpen:!t.lockOpen;this._openingDialog={...t,contactOpen:"contact"===e&&i,lockOpen:"lock"===e&&i}}_filterOpeningEntities(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contactFilter:t}:{...i,lockFilter:t})}_selectOpeningEntity(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contact:t,contactOpen:!1,contactFilter:""}:{...i,lock:t,lockOpen:!1,lockFilter:""})}_mergeClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms,s=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!s?.id)return;const n=s.id;if(!this._mergeSel||this._mergeSel===n)return void(this._mergeSel=this._mergeSel===n?null:n);const o=i.find(e=>e.id===this._mergeSel),a=o?hi(o):null,r=hi(s),l=a&&r?function(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=si(Ii(e),Ii(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return s.length>=3?s:null}(a,r):null;if(!l)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:n,poly:l,pick:"a"},this._mergeSel=null}_commitMerge(){const e=this._mergeDialog,t=this._curSpaceCfg;if(!e||!t||!this._spaceModel())return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===e.pick?e.aId:e.bId,o="a"===e.pick?e.bId:e.aId,a=t.rooms.find(e=>e.id===n);if(!a)return void(this._mergeDialog=null);a.poly=e.poly.map(e=>[e[0]/lv,e[1]/s]),delete a.x,delete a.y,delete a.w,delete a.h,t.rooms=t.rooms.filter(e=>e.id!==o);const r=this._commitPhysicalGeometry(this._t("history.merge_rooms"),i);this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),r&&this._showToast(this._t("toast.rooms_merged",{name:a.name||""}))}_splitClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms;if(!this._splitSel){const t=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!t?.id)return;return void(this._splitSel={roomId:t.id,pts:[]})}const s=i.find(e=>e.id===this._splitSel.roomId),n=s?hi(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,a=6*this._gridPitch,r=function(e,t){if(!t||t.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[a+g*h,r+g*d]}return s}(r,n,this._gridPitch)||r,c=r&&l&&Math.hypot(r[0]-e[0],r[1]-e[1])<=a?l:null,h=!!c&&bi(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const t=this._snap(e);return wi(t,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,t]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=zi(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=Ti(u)>=Ti(_)?u:_,g=m===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:m,newPoly:g},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}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)return;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(e)));if("opening"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(e));const t="draw"===this._tool&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!t&&!i)return;const s=this._svgPoint(e);if(t){const t=this._resolvePlanDrawPoint(s,e.shiftKey);return this._planSnapHover={contextKey:t.contextKey,candidate:t.candidate,conflicts:t.conflicts},this._syncPlanSnapConflictMarkers(t.conflicts),this._path.length?void(this._cursorPt=t.point):void this._syncPlanSnapActiveMarker(t.candidate)}this._cursorPt=this._snap(s)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&Sf(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&(this._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(e){const t=this._wallFaceBatch;if(!t)return;const i=t.candidates[t.index];if(!i)return;if(!e&&(i.existing||i.repair)&&1===t.candidates.length)return this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",void this.requestUpdate();const s=e?{candidate:i,create:!0,name:this._nameSel.trim(),area:this._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:i,create:!1},n=[...t.decisions,s],o=t.index+1;if(o"number"==typeof e&&Number.isFinite(e)&&e>=0?e:null,n=.02*this._gridPitch,o=this._spaceModel();if(!o)return ul;for(const t of this._planSnapGeometrySnapshot().value.segments){if("room"===t.sourceKind||Hs(e,[t.a[0],t.a[1],t.b[0],t.b[1]])>n)continue;if("partition"===t.sourceKind)return s(o.partitions.find(e=>e.id===t.sourceId)?.cm)??ul;const i=t.sourceId.lastIndexOf(":"),a=i>=0?t.sourceId.slice(0,i):t.sourceId,r=i>=0?Number(t.sourceId.slice(i+1)):-1;return s(o.room_drafts.find(e=>e.id===a)?.segments[r]?.cm)??ul}const a=iu(Math.max(0,t.length-1),i,this._drawWallCm,ul);for(let i=0;i+1[...e]);if(!t?.sourceKey.startsWith("active:"))return i;const s=/:(\d+)$/.exec(t.sourceKey),n=s?Number(s[1])+("b"===t.endpoint?1:0):-1;return n>=0&&nt.key===e.sourceKey),o=s.find(t=>t.key===e.targetSourceKey);if(!n||!o)return!1;const a="a"===e.endpoint?n.a:n.b;return!(Math.hypot(a[0]-e.from[0],a[1]-e.from[1])>i)&&(!(Hs(e.to,[o.a[0],o.a[1],o.b[0],o.b[1]])>i)&&!function(e,t){if(!e.sourceKey.startsWith("static:partition|"))return!1;const i=e.sourceKey.slice(17).split("|")[0];return!!i&&t.some(e=>"partition"===e.host?.kind&&e.host.id===i)}(e,this._curSpaceCfg?.openings||[]))}_applyWallRepair(e,t){const i=this._curSpaceCfg;if(!i||!this._validateWallRepair(e,t.activePath))return!1;const s=t=>{t[0]=e.to[0]/lv,t[1]=e.to[1]/lv};if(e.sourceKey.startsWith("active:")){const n=/:(\d+)$/.exec(e.sourceKey),o=n?Number(n[1])+("b"===e.endpoint?1:0):-1;if(o<0||o>=t.activePath.length)return!1;t.activePath[o]=[...e.to];const a=(i.room_drafts||[]).find(e=>e.id===t.activeDraftId);return a?.points?.[o]&&s(a.points[o]),!0}if(!e.sourceKey.startsWith("static:"))return!1;const[n,o]=e.sourceKey.slice(7).split("|");if("partition"===n){const t=(i.partitions||[]).find(e=>e.id===o);if(!t)return!1;const n=[t.a[0]*lv,t.a[1]*lv],a=[t.b[0]*lv,t.b[1]*lv],r=Math.hypot(n[0]-e.from[0],n[1]-e.from[1])<=2e-4*this._gridPitch?t.a:Math.hypot(a[0]-e.from[0],a[1]-e.from[1])<=2e-4*this._gridPitch?t.b:null;return!!r&&(s(r),!0)}if("draft"===n){const t=o.lastIndexOf(":"),n=t>=0?o.slice(0,t):"",a=t>=0?Number(o.slice(t+1)):-1,r=(i.room_drafts||[]).find(e=>e.id===n);if(!r?.points?.[a]||!r?.points?.[a+1])return!1;const l=[a,a+1].find(t=>Math.hypot(r.points[t][0]*lv-e.from[0],r.points[t][1]*lv-e.from[1])<=2e-4*this._gridPitch);return null!=l&&(s(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const e=this._wallFaceBatch,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=(e,t)=>{this._showToast(this._t(e,t)),this._roomDialogCancel()},n=e.decisions.filter(e=>e.create),o=n.map(e=>e.candidate.ring),a=i.rooms;for(let e=0;ei.id!==t&&!!hi(i)&&Di(o[e],hi(i)));if(i)return void s("toast.room_overlap",{name:i.name||""})}if((t.rooms||[]).length+n.length>400)return void s("toast.physical_limit");if(n.some(e=>e.candidate.split&&!t.rooms.some(t=>t.id===e.candidate.split.roomId)))return void s("toast.contour_cannot_close");const r=n.map(e=>e.candidate.repair).filter(e=>!!e);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],e.activePath))return void s("toast.wall_repair_changed");const l=this._activePathWithRepair(e.activePath,r[0]),c=2e-4*this._gridPitch,h=new Map;for(let t=0;te.candidate.atomKeys)),t=n.some(e=>e.candidate.consumeAllActive);for(const i of $u(this._wallGraphSources(l),c)){const s=i.sourceKeys.find(e=>h.has(e));!s||t||e.has(i.key)||d.push({a:i.a,b:i.b,cm:h.get(s)})}}else d.push(...su(l,iu(l.length-1,e.activeCms,this._drawWallCm,ul)));if((t.partitions||[]).length+d.length>pv)return void s("toast.physical_limit");const p=n.map(e=>e.candidate.ring.map(()=>"")),u=e.activeDraftId?(t.room_drafts||[]).find(t=>t.id===e.activeDraftId):null,_=this._draftSegmentsForPath(e.activePath,u,e.activeCms),m=(e,t)=>{const i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`,s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`;return i<=s?`${i}|${s}`:`${s}|${i}`};for(let t=0;t<_.length;t++){const i=_[t]?.id;if(!i)continue;const s=e.activePath[t],o=e.activePath[t+1],a=[s[0],s[1],o[0],o[1]],r=[];n.forEach((e,t)=>e.candidate.ring.forEach((i,s)=>{const n=e.candidate.ring[(s+1)%e.candidate.ring.length];Hs(i,a)<=c&&Hs(n,a)<=c&&r.push({kind:"room",owner:t,edge:s,a:i,b:n})})),d.forEach((e,t)=>{Hs(e.a,a)<=c&&Hs(e.b,a)<=c&&r.push({kind:"partition",owner:t,edge:0,a:e.a,b:e.b})});const l=[(s[0]+o[0])/2,(s[1]+o[1])/2];r.sort((e,t)=>{const i=[e.a[0],e.a[1],e.b[0],e.b[1]],n=[t.a[0],t.a[1],t.b[0],t.b[1]];return(Hs(l,i)<=c?0:1)-(Hs(l,n)<=c?0:1)||(Hs(s,i)<=c?0:1)-(Hs(s,n)<=c?0:1)||m(e.a,e.b).localeCompare(m(t.a,t.b))});const h=r[0];if(!h)continue;const u=m(h.a,h.b);for(const e of r)m(e.a,e.b)===u&&("room"===e.kind?p[e.owner][e.edge]=i:d[e.owner].id=i)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],e))return void s("toast.wall_repair_changed");const f=n.some(e=>!!e.candidate.split),v=f?fc(i.rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,lv):null,y=f&&Array.isArray(t.walls)?t.walls:null;for(const e of n){const i=e.candidate.split;if(!i)continue;const n=t.rooms.find(e=>e.id===i.roomId);if(!n)return void s("toast.contour_cannot_close");n.poly=i.mainPoly.map(e=>[e[0]/lv,e[1]/this._spaceH]),delete n.x,delete n.y,delete n.w,delete n.h}const b=Date.now().toString(36),w=[];if(n.forEach((e,i)=>{const s=e.area?this.hass.areas[e.area]?.name:"",n={id:`r${b}-${i}`,name:e.name||s||this._t("room.default_name"),area:e.area||null,poly:e.candidate.ring.map(e=>[e[0]/lv,e[1]/this._spaceH]),...p[i].some(Boolean)?{wall_ids:p[i]}:{},...e.settings?{settings:JSON.parse(JSON.stringify(e.settings))}:{}};t.rooms.push(n),w.push({room:n,decision:e})}),e.activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==e.activeDraftId),t.room_drafts.length||delete t.room_drafts),d.length&&(t.partitions||=[],d.forEach((e,i)=>t.partitions.push({id:e.id||`partition-${b}-${i}`,a:[e.a[0]/lv,e.a[1]/lv],b:[e.b[0]/lv,e.b[1]/lv],cm:e.cm}))),f){const e=this._normalizeWalls(v,this._openCuts());e.length?t.walls=e:y?.length?t.walls=y:delete t.walls}if(w.length){this._cfgEpoch++;const i=this._openCuts(),s=this._spaceModel();if(!s)return;let n=t.walls;for(const{room:t,decision:o}of w){const a=o.candidate.ring,r=this._wallSourceCmAt(a[0],e.activePath,e.activeCms);n=ql(n,s.rooms,t.id,r,this._wallKeyPitch,i,lv);for(const o of gc(s.rooms,n,i,this._wallKeyPitch,this._cellCm,this._gridPitch,lv)){if(o.roomId!==t.id||"outer"!==o.kind)continue;const i=[(o.a[0]+o.b[0])/2,(o.a[1]+o.b[1])/2];n=Ll(n,o.a,o.b,this._wallSourceCmAt(i,e.activePath,e.activeCms),this._wallKeyPitch,lv)}}const o=this._normalizeWalls(n,i);o.length?t.walls=o:delete t.walls}if(!this._commitPhysicalGeometry(this._t("history.wall_face_batch"),g))return;delete this._resumeDraftBySpace[this._space],this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices();const k=new Set(n.map(e=>e.area).filter(e=>!!e));if(k.size){const e={...this._layout};for(const t of this._devices){if(!k.has(t.area||"")||t.space!==this._space)continue;if(this._layout[t.id])continue;const i=this._defPos[t.id];i&&(e[t.id]={s:this._space,x:i.x/lv,y:i.y/lv},this._dirtyPos.add(t.id))}this._layout=e,this._persistLayout()}this._showToast(this._t(n.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:n.length}))}_commitRoom(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return;const i=this._geometrySnapshot(),s=this._spaceH,n=!!this._pendingSplit,o=n?fc(t.rooms,e.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,lv):null,a=n&&Array.isArray(e.walls)?e.walls:null;let r;if(this._pendingSplit){const t=e.rooms.find(e=>e.id===this._pendingSplit.roomId);if(!t)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);t.poly=this._pendingSplit.mainPoly.map(e=>[e[0]/lv,e[1]/s]),delete t.x,delete t.y,delete t.w,delete t.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const l=this._areaSel?this.hass.areas[this._areaSel]?.name:"",c={id:"r"+Date.now().toString(36),name:this._nameSel||l||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(e=>[e[0]/lv,e[1]/s]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!n&&this._activeDraftId){const t=Array.isArray(e.room_drafts)?e.room_drafts.find(e=>e.id===this._activeDraftId):null,i=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),s=this._draftSegmentsForPath(this._path,t,i);s.length===r.length&&s.every(e=>!!e.id)&&(c.wall_ids=s.map(e=>e.id))}if(e.rooms.push(c),!n&&this._activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),n){const t=this._normalizeWalls(o,this._openCuts());t.length?e.walls=t:a?.length?e.walls=a:delete e.walls}if(!n){const t=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),i=t[0];if(null!=i){this._cfgEpoch++;const s=this._openCuts(),n=this._spaceModel();if(!n)return;let o=ql(e.walls,n.rooms,c.id,i,this._wallKeyPitch,s,lv);for(const e of gc(n.rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,lv)){if(e.roomId!==c.id||"outer"!==e.kind)continue;const i=[(e.a[0]+e.b[0])/2,(e.a[1]+e.b[1])/2],s=r.findIndex((e,t)=>{const s=r[(t+1)%r.length];return Hs(i,[e[0],e[1],s[0],s[1]])<=.02*this._gridPitch});s>=0&&(o=Ll(o,e.a,e.b,t[s],this._wallKeyPitch,lv))}o=this._normalizeWalls(o,s),o.length?e.walls=o:delete e.walls}}const h=this._commitPhysicalGeometry(this._t(n?"history.split_room":"history.add_room"),i);this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const d=this._areaSel;if(this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices(),!h)return;let p=0;if(d){const e=lv,t={...this._layout};for(const i of this._devices){if(i.area!==d||i.space!==this._space)continue;if(p++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(t[i.id]={s:this._space,x:s.x/lv,y:s.y/e},this._dirtyPos.add(i.id))}this._layout=t,this._persistLayout()}const u=this._model.find(e=>e.id===this._space)?.rooms.length||0;this._showToast(d?this._t("toast.room_saved",{n:u,added:p}):this._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this._wallRepairDiagnostic=null,this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),this._roomDialog=!1,this._pendingSplit=null,this._wallFaceBatch=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const e=this._resumeDraftBySpace[this._space];if(!e)return;const t=this._spaceModel()?.room_drafts.find(t=>t.id===e);t?(this._activeDraftId=e,this._path=t.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,t.segments.map(e=>e.cm),t.id),this._clearPlanSnapHover()):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){if(this._roomDialog=!1,this._roomEditId)return this._roomEditId=null,this._nameSel="",void(this._areaSel="");if(this._wallFaceBatch){const e=this._wallFaceBatch;return this._wallFaceBatch=null,this._path=e.activePath.map(e=>[...e]),this._draftSegmentCms=[...e.activeCms],this._activeDraftId=e.activeDraftId,this._nameSel="",this._areaSel="",void this.requestUpdate()}if(this._pendingSplit)return this._pendingSplit=null,void(this._splitSel=null);this._undoPoint()}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||""))}_openMarkerDialog(e){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,e&&this._ackNewDevice(e.id),this._norm)if(e){const t=e.marker,i=Object.prototype.hasOwnProperty.call(t||{},"is_light"),s=Object.prototype.hasOwnProperty.call(t||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(t||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(t||{},"value_badge"),a=Object.prototype.hasOwnProperty.call(t||{},"tap_action"),r=Object.prototype.hasOwnProperty.call(t||{},"toggle_entity"),l=ks(t?.glow_color),c=this._devicePresentation(e,!0).valueBadge,h=Wo(this._planHass,e,this._devices),d=jo(this._planHass,e,h);this._markerDialog={devId:e.id,name:e.name,binding:"virtual"===e.bindingKind?"virtual":e.bindingKind+":"+e.bindingRef,bindingMode:"virtual"===e.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===e.bindingKind&&!!this._fullRegistryHass.entities[e.bindingRef||""]?.device_id,bindingFilter:"",icon:e.marker?.icon||"",autoIcon:e.icon||"",display:Bi(e.marker?.display),rippleColor:Ie(e.marker?.ripple_color,""),rippleSize:Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):1.5,size:Number(e.marker?.size)>0?Number(e.marker.size):1,angle:Number(e.marker?.angle)||0,tapAction:vp(e.marker?.tap_action,e.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:a,originalTapAction:e.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:t?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:r,originalToggleEntity:t?.toggle_entity,tapTarget:e.marker?.tap_target||"",tapConfirm:!0===e.marker?.tap_confirm,runFilter:"",controls:Zn(e.marker?.binding,e.marker?.controls,e.entities),controlsFilter:"",lightRole:!0===t?.is_light?"always":!1===t?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:t?.is_light,lightEntity:t?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:t?.light_entity,glowMode:null!=l?.bri?"fixed":l?"color":"auto",glowColor:l?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(l?.bri??1))),glowColorDrafted:!!l,glowBrightnessDrafted:null!=l?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!l||null===t?.glow_color),originalGlowColor:l||(null===t?.glow_color?null:void 0),valueBadgeEnabled:o?!0===t?.value_badge?.enabled:!!c,valueBadgeSource:t?.value_badge?.source||c?.source||d,valueBadgePosition:t?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:t?.value_badge,useClimateTemp:!0===e.marker?.use_climate_temp,glowRadius:Number(e.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(e.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(e.marker.glow_radius_cm))/100):"",model:e.model||"",link:e.link||"",description:e.description||"",pdfs:[...e.pdfs||[]],room:e.marker?.room_id?e.space+"#@"+e.marker.room_id:e.space&&e.area?e.space+"#"+e.area:"",hideFromPlan:!0===e.marker?.hidden,busy:!1}}else this._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._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,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const e=[];for(const t of Gi)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(t+".")&&this._planEntityAvailable(i)&&e.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+t)});return e.sort((e,t)=>e.sub.localeCompare(t.sub)||e.label.localeCompare(t.label))}_bindingCandidates(){const e=this._planHass,t=wo(this._markers),i=new Set(this._markers.filter(e=>e.removed).map(e=>e.binding)),s=new Set;for(const e of this._devices)e.id!==this._markerDialog?.devId&&("device"===e.bindingKind&&e.bindingRef&&s.add("device:"+e.bindingRef),"entity"===e.bindingKind&&e.bindingRef&&s.add("entity:"+e.bindingRef));const n=new Set;for(const e of this._devices)"device"===e.bindingKind&&e.name&&n.add(e.name.trim()+"|"+(e.area||""));const o=[];for(const t of Object.values(e.devices)){if("service"===t.entry_type)continue;const e="device:"+t.id;if(s.has(e))continue;const a=(t.name_by_user||t.name||t.id).trim();e!==this._markerDialog?.binding&&!i.has(e)&&n.has(a+"|"+(t.area_id||""))||o.push({value:e,label:a,sub:(t.model||this._t("marker.sub_device"))+("Group"===t.model?this._t("marker.sub_z2m_group"):"")})}const a=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,r]of Object.entries(e.entities)){const l="entity:"+n;if(s.has(l))continue;if(ko(e,n,t)&&!i.has(l))continue;const c=a.has(r.platform),h="group"===r.platform;if(!c&&!h)continue;if(r.hidden&&!i.has(l))continue;const d=e.states[n];o.push({value:l,label:r.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===r.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(e=>e.value));for(const[a,r]of Object.entries(e.entities)){const l="entity:"+a;if(s.has(l)||n.has(l)||r.hidden&&!i.has(l))continue;const c=!!r.device_id&&t.devices.has(r.device_id);if(ko(e,a,t)&&!i.has(l)&&!c)continue;const h=e.states[a],d=r.name||h?.attributes?.friendly_name||a,p=r.device_id?e.devices[r.device_id]:null,u=p&&(p.name_by_user||p.name)||"";o.push({value:l,label:d,sub:a.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const r=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=r?o.filter(e=>(e.label+" "+e.sub+" "+e.value).toLowerCase().includes(r)):o;return l.sort((e,t)=>e.label.localeCompare(t.label)),l.slice(0,200)}_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=rh(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:uh([[[...t,t[0]]]])};const s=e.id||`#${i.rooms.indexOf(e)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._resize.preview){const e=Yf(this._cleanFloorCache,n);if(e.hit)return e.value}const o=t.map(e=>e[0]),a=t.map(e=>e[1]),r=[Math.min(...o),Math.min(...a),Math.max(...o),Math.max(...a)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=r[0]&&Math.min(...t)<=r[2]&&Math.max(...i)>=r[1]&&Math.min(...i)<=r[3]}),c=l.length?ph(t,l):null,h={floor:t,geom:c,path:c?ih(c):"",area:uh(c||[[[...t,t[0]]]])};return this._resize.preview||Jf(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(e),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===t){const e=s.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":bo(s,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=s.entities?.[i],t=this.hass.states?.[i],n=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":bo(s,n,"",[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||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(t){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===t[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},s=t[3].split(",").filter(Boolean).map(e=>i[e]?this._t(i[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||t[1],fields:s})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"").match(/margin_cm=([^;]*)/),i=t?Number(t[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:li(i,this._imperial)})}if(e.message)return e.message;if(e.error)return e.error;if(null!=e.code)return this._t("err.code",{code:e.code});try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){const t=e?.code??e?.error;if("string"==typeof t){const e=`backup.error.${t}`,i=this._t(e);if(i!==e)return i}return this._errText(e)}async _pickMarkerFiles(e){const t=e.target,i=t.files?[...t.files]:[];if(t.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const e of i)try{const t=new FormData;t.append("marker_id",s),t.append("file",e,e.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:t}):await fetch("/api/houseplan/upload",{method:"POST",body:t,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const e={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(e[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||e.name,url:o.url})}catch(t){this._showToast(this._t("toast.file_failed",{name:e.name,err:this._errText(t)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(t=>t.url!==e)})}_markerLightFields(e){const t={};if(e.lightRoleTouched?"always"===e.lightRole?t.is_light=!0:"never"===e.lightRole&&(t.is_light=!1):e.originalHasIsLight&&(t.is_light=e.originalIsLight??null),e.lightEntityTouched?e.lightEntity&&(t.light_entity=e.lightEntity):e.originalHasLightEntity&&(t.light_entity=e.originalLightEntity??null),e.glowTouched){if("auto"!==e.glowMode){const i={c:Ie(e.glowColor,this._fillColors.glow_light.c)};"fixed"===e.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(e.glowBrightness)/100))),t.glow_color=i}}else e.originalHasGlowColor&&(t.glow_color=e.originalGlowColor??null);return t}_markerTapActionFields(e){return e.tapActionTouched?{tap_action:e.tapAction||null}:e.originalHasTapAction?{tap_action:e.originalTapAction??null}:{}}_markerToggleEntityFields(e){return(t={touched:e.toggleEntityTouched,originalHas:e.originalHasToggleEntity,original:e.originalToggleEntity,value:e.toggleEntity}).touched?t.value?{toggle_entity:t.value}:{}:t.originalHas?{toggle_entity:t.original??null}:{};var t}async _saveMarker(){const e=this._markerDialog;if(!e||e.busy)return;const t=this._effectiveMarkerTapAction(e);if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return;if("virtual"===e.binding&&!e.name.trim())return void this._showToast(this._t("toast.virtual_name_required"));if("run"===t&&!e.tapTarget)return void this._showToast(this._t("toast.run_target_required"));if(e.valueBadgeTouched&&e.valueBadgeEnabled&&!e.valueBadgeSource)return void this._showToast(this._t("toast.value_badge_source_required"));if("ha"===e.bindingMode){const t=this._bindingStatus(e.binding),i=e.devId?this._markers.find(t=>t.id===e.devId):null,s=!i||i.binding!==e.binding;if("active"!==t.kind&&s)return void this._showToast(this._t("ha_disabled"===t.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===t.kind&&!0===i?.hidden&&!e.hideFromPlan)return this._markerDialog={...e,hideFromPlan:!0},void this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const i=this._serverCfg;if(!i)return;const s=i.markers||[],n=qs(e.room);let o=n?.space||null;const a=n?.area||null,r=n?.roomId||null,l=Fi(e.binding,e.devId,()=>"v_"+Date.now().toString(36)),c=e.devId,h=c?this._devices.find(e=>e.id===c):null,d=n?.space||h?.space||null,p=d?this._spaceModelById(d):this._spaceModel();if(!p)return;const u=p.id;"virtual"!==e.binding||o||(o=u),this._markerDialog={...e,busy:!0};try{const n="virtual"===e.binding?[]:s.filter(t=>t.removed&&t.binding===e.binding).map(e=>e.id),d=n.length>0,_=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),m=s.find(e=>e.id===l||e.id===c)?.vacuum||null,g={id:l,vacuum:m,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle?e.angle:null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===t&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:_.length?_:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Kf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:!!e.hideFromPlan};("virtual"===e.binding||e.room)&&(g.space=o,g.area=a,g.room_id=r);const f=h?.marker?.room_id??null,v=!!e.room&&null!=h&&(h.space!==o||h.area!==a||f!==r);let y=!1;const b=e.uploadId||c;if(b&&b!==l&&g.pdfs?.length)try{const e=await this.hass.callWS({type:"houseplan/files/migrate",from_id:b,to_id:l}),t=e?.mapping||{};g.pdfs=function(e,t,i,s){if(!t||!i||t===i)return e;const n="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(n))return e;const t=e.url.split(n)[1]||"",[i,a]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(s){const t=s[decodeURIComponent(i)]??s[i];return t?{...e,url:e.url.split(n+i)[0]+o+encodeURIComponent(t)+a}:e}return{...e,url:e.url.split(n).join(o)}})}(g.pdfs,b,l,t),y=Object.keys(t).length>0}catch(e){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(e)}))}i.markers=s,c&&c!==l&&(i.markers=function(e,t,i){if(!t||!i||t===i)return[...e];const s=`marker:${t}`,n=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===s?n:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===s?{...e.value_badge,source:{...e.value_badge.source,ref:n}}:e.value_badge;return t===e.controls&&i===e.value_badge?e:{...e,controls:t,value_badge:i}})}(i.markers,c,l)),c&&c!==l&&"derived_marker_state"===g.value_badge?.source?.kind&&g.value_badge.source.ref===`marker:${c}`&&(g.value_badge.source={kind:"derived_marker_state",ref:`marker:${l}`}),i.markers=i.markers.filter(e=>e.id!==l&&e.id!==c&&("virtual"===g.binding||e.binding!==g.binding)),i.markers.push(g);let w=null;const k=c?this._layout[c]:null,x=k?{s:k.s||h?.space||this._space,x:k.x,y:k.y}:c&&h&&this._defPos[c]?this._normPos(h.space,this._defPos[c].x,this._defPos[c].y):null;if(!d&&x&&x.s===u)l===c&&this._layout[l]&&!v||(w={s:x.s,x:x.x,y:x.y},this._layout={...this._layout,[l]:w});else if(d||!this._layout[l]||v){let e=p.vb[0]+p.vb[2]/2,t=p.vb[1]+p.vb[3]/2;const i=r?p.rooms.find(e=>e.id===r):a?p.rooms.find(e=>e.area===a):void 0;i&&([e,t]=this._roomCenter(i)),w=this._normPos(u,e,t),this._layout={...this._layout,[l]:w}}if(await this._saveConfigNow(),w){const e=hl(w);this._layout={...this._layout,[l]:e},this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:l,pos:e}))}const $=new Set(n);c&&c!==l&&$.add(c),$.delete(l);for(const e of $)delete this._layout[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{});y&&b&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:b}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _deleteMarker(){const e=this._markerDialog;if(!e||e.busy||!e.devId)return;const t=e.devId?this._devices.find(t=>t.id===e.devId):null;if(!t)return;const i=e.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===t.bindingKind?"virtual":t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:"";if(!o)return;const a=function(e,t,i,s){const n=new Set([t]),o=e.filter(e=>{const o=e.id===t||!s&&e.binding===i;return o&&n.add(e.id),!o});return{markers:s?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,t.id,o,"virtual"===t.bindingKind);var r,l;s.markers=(r=a.markers,l=a.cleanupIds,r.map(e=>{if(!Array.isArray(e.controls))return e;const t=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&l.has(e.slice(7))));return t.length===e.controls.length?e:{...e,controls:t.length?t:null}}));const c=a.cleanupIds;this._markerDialog={...e,busy:!0};try{await this._saveConfigNow();for(const e of c){delete this._layout[e],delete this._defPos[e],this._dirtyPos.delete(e),this._sentPos.delete(e);const t=this._activityRt.get(e);t&&clearTimeout(t.timer),this._activityRt.delete(e),this._vacRt.delete(e),delete this._vacSrvTrails[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:e}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:e}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===t.id&&this._closeInfoCard(),this._selId===t.id&&(this._selId=null),this._drag?.id===t.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(e){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}_normPos(e,t,i){return{s:e,x:t/lv,y:i/lv}}_openSpaceDialog(e,t){if(this._serverStorage&&this._serverCfg)if("edit"===e){const i=this._serverCfg.spaces.find(e=>e.id===t);if(!i)return;const s=cs(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?rs(i.settings.custom_fill):null,o="none"===s.fill?{...n||as,a:0}:n;this._spaceDialog={mode:e,spaceId:t,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,zeroWallStyle:f_(i),displayTouched:!0,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Pr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:xh(Number(i.cell_cm)>0?Number(i.cell_cm):5,this._imperial),cellCmTouched:!1,busy:!1}}else this._spaceDialog={mode:e,title:"",planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:{...as,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(e){const t=e.target,i=t.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let e=0;e{const t=new Image;t.onload=()=>e(t.naturalWidth&&t.naturalHeight?t.naturalWidth/t.naturalHeight:1.414),t.onerror=()=>e(1.414),t.src=r});URL.revokeObjectURL(r),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:a,aspect:l,name:i.name}}}_useServerPlan(e){const t=this._spaceDialog;t&&(this._spaceDialog={...t,planUrl:e,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(e))}async _readPlanAspect(e){for(let t=0;t<40;t++){const t=this._display(e);if(t){const i=await new Promise(e=>{const i=new Image;i.onload=()=>e(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>e(0),i.src=t}),s=this._spaceDialog;return s&&s.planUrl===e&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(e=>setTimeout(e,150)),this._spaceDialog?.planUrl!==e)return 0}return 0}async _deleteServerPlan(e){if(confirm(this._t("confirm.delete_plan",{name:e})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:e});const t=this._spaceDialog;t?.saved&&(this._spaceDialog={...t,saved:t.saved.filter(t=>t.name!==e)})}catch(e){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(e)}))}}_renderServerPlans(e){if(e.savedBusy)return W`
${this._t("space.loading")}
`;const t=e.saved||[];if(!t.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingAt(e){if(!this._openingsR.length)return null;const t=this._openingsR.flatMap(t=>{const i=t.angle*Math.PI/180,s=e[0]-t.rx,n=e[1]-t.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>t.rlen/2+wh(12,this._cellCm)?[]:[{o:t,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!t.length)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s).value;return t.find(({o:e,localY:t})=>{const i="gate"===e.type?!e.flip_v:e.flip_v,s=e.partitionHost||this._spaceWalls.length||"gate"===e.type?this._openingFace(e,n,!!i):{ox:0,oy:0,cm:0,side:-1},o=Uu({type:e.type,length:e.rlen,angle:e.angle,amount:this._openingAmt(e),flipH:!!e.flip_h,flipV:!!e.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:s});return Math.abs(t)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(e){const t=this._openingPreset;if("opening"!==this._tool||!t)return null;const i=this._spaceModel();if(!i)return null;const s=this._openCuts(),n=this._openingWallIndexFor(i,s),o=`${n.key}|partitions:${this._cfgEpoch}`;this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===o||(this._openingPlacementIntervalsCache={key:o,value:[...gc(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,cv),...Fp(i.partitions,this._cellCm,this._gridPitch)]}),this._openingDimensionContextCache&&this._openingDimensionContextCache.key===o||(this._openingDimensionContextCache={key:o,value:zf({rooms:i.rooms,walls:this._spaceWalls,openCuts:s,partitions:i.partitions,roomOpenings:this._roomWallOpeningInputs(),partitionCuts:this._partitionOpeningCuts(i),pitch:this._wallKeyPitch,cellCm:this._cellCm,gridPitch:this._gridPitch,coordScale:cv,epsilon:2e-4*this._gridPitch})});const a=Sf({pointer:[e[0],e[1]],preset:t,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(t.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender("touch"===this._pointerModality.modality||"pen"===this._pointerModality.modality?10:6),gridStep:this._gridPitch});this._openingJambBlockCm=a.jambBlockedTarget?a.jambBlockedTarget.physicalHalfWidth/this._gridPitch*this._cellCm:null;const r=a.candidate;if(!r)return null;const l="gate"===r.type?!r.flipV:r.flipV;let c;if(r.host){const e=Tp({id:"preview",type:r.type,x:r.x/cv,y:r.y/cv,angle:r.angle,length:r.renderedLength/cv,host:r.host},i.partitions,cv,this._cellCm,this._gridPitch).resolved;c=e?zp(e,l):{ox:0,oy:0,cm:0,side:-1}}else c=Uc(n.value,{x:r.x,y:r.y,angle:r.angle,length:r.renderedLength,flip_v:l});const h="mi"===this.hass?.config?.unit_system?.length,d=Bf(r,this._openingDimensionContextCache.value).map(e=>({x:e.label[0],y:e.label[1],text:li(e.distance/this._gridPitch*this._cellCm,h),dimension:e}));return{...r,face:c,measure:{labels:d,guide:r.measure.guide}}}_activateOpeningPlacement(e){this._activateMarkupTool("opening"),"opening"===this._tool&&(this._openingPreset=function(e,t){return{type:e,lengthCm:wf(e),flipH:!1,flipV:!1,revision:t}}(e,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null)}_clearOpeningPlacement(e){this._openingHoverCandidate=null,this._openingJambBlockCm=null,e&&(this._openingPreset=null,this._openingRebindId=null)}_openingClick(e){const t=this._spaceModel();if(!t)return;const i=this._openingRebindId?null:this._openingAt(e);if(i)return void this._editOpening(i);const s=this._openingPreset;if(!s)return;const n=this._openingHoverCandidate,o=n&&Mf(n,[e[0],e[1]],s.revision,this._cfgEpoch)?n:this._resolveOpeningPlacement(e);if(!o){if(null!=this._openingJambBlockCm)return void this._showToast(this._t("opening.partition_jamb_margin",{distance:li(this._openingJambBlockCm,this._imperial)}));const i=1.5*this._gridPitch,s=ui(e,t.rooms,i);return s&&function(e,t,i,s,n){for(const o of s)if(!(Ls([e,t],o)>n)&&El([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),i)?void this._showToast(this._t("toast.opening_on_zero_wall")):void this._showToast(this._t("toast.opening_no_wall"))}const a=this._openingRebindId?this._curSpaceCfg?.openings?.find(e=>e.id===this._openingRebindId):null;this._openingDialog={...a?{id:a.id,type:a.type,lengthCm:Math.round(a.length*cv/this._gridPitch*this._cellCm),lengthTouched:!1,contact:a.contact||"",lock:a.lock||"",invert:!!a.invert,flipH:!!a.flip_h,flipV:!!a.flip_v}:{type:o.type,lengthCm:o.lengthCm,contact:"",lock:"",invert:!1,flipH:o.flipH,flipV:o.flipV},...o.host?{host:o.host}:{},x:o.x,y:o.y,angle:o.angle},this._openingRebindId=null,this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(e){this._openingDialog={id:e.id,type:e.type,lengthCm:Math.round(e.rlen/this._gridPitch*this._cellCm),lengthTouched:!1,contact:e.contact||"",lock:e.lock||"",invert:!!e.invert,flipH:!!e.flip_h,flipV:!!e.flip_v,..."partition"===e.host?.kind?{host:{...e.host}}:{},x:e.rx,y:e.ry,angle:e.angle}}_opPointerDown(e,t){if("plan"===this._mode&&this._spaceModel()&&"resize"!==this._tool){e.preventDefault(),e.stopPropagation();try{fv(e)}catch{}this._opDrag={id:t.id,moved:!1,sx:e.clientX,sy:e.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;if(Math.abs(e.clientX-this._opDrag.sx)+Math.abs(e.clientY-this._opDrag.sy)<=3)return;const i=this._spaceModel();if(!i)return;const s=this._svgPoint(e),n=this._curSpaceCfg,o=n?.openings?.find(e=>e.id===t.id);if(!o)return;if("partition"===o.host?.kind){const e=i.partitions.find(e=>e.id===o.host.id);if(!e)return;const t=e.b[0]-e.a[0],n=e.b[1]-e.a[1],a=Math.hypot(t,n);if(!(a>1e-9))return;const r=t/a,l=n/a;if(Math.abs((s[0]-e.a[0])*l-(s[1]-e.a[1])*r)>4*this._gridPitch)return;const c=o.length*cv/2+Dp(e,this._cellCm,this._gridPitch);let h=(s[0]-e.a[0])*r+(s[1]-e.a[1])*l;if(h=Math.round(h/this._gridPitch)*this._gridPitch,h=Math.max(c,Math.min(a-c,h)),a<2*c-1e-9)return;const d=h/a,p=(e.a[0]+r*h)/cv,u=(e.a[1]+l*h)/this._spaceH;let _=180*Math.atan2(n,t)/Math.PI;_>=90?_-=180:_<-90&&(_+=180);const m=o.host.t!==d||o.x!==p||o.y!==u||o.angle!==_;return this._opDrag.moved=!0,m&&(this._opDrag.dirty=!0),o.host={...o.host,t:d},o.x=p,o.y=u,o.angle=_,this._opMeasure=null,m&&this._cfgEpoch++,void this.requestUpdate()}const a=ui(s,i.rooms,4*this._gridPitch);if(!a)return;this._opDrag.moved=!0;const r=this._opRuler(a,o.length*cv);this._opMeasure=r.measure;const l=r.x/cv,c=r.y/this._spaceH,h=o.x!==l||o.y!==c||o.angle!==a.angle;if(h&&(this._opDrag.dirty=!0),o.x=l,o.y=c,o.angle=a.angle,Number(this._serverCfg?.model_version||0)>=9){const e=tg(o,n?.wall_segments||[]);e?o.host=e:delete o.host}h&&this._cfgEpoch++,this.requestUpdate()}_opRuler(e,t){const i=this._spaceModel()?.rooms||[],s=this._gridPitch/2;let n=e.x,o=e.y,a=_i([n,o],e.angle,t,i,s);if(a&&a.centered&&(n!==a.wallCenter[0]||o!==a.wallCenter[1]))[n,o]=a.wallCenter,a=_i([n,o],e.angle,t,i,s);else if(a){const[r,l]=a.wallA,[c,h]=a.wallB,d=c-r,p=h-l,u=Math.hypot(d,p);if(u>0){const c=this._gridPitch,h=Math.min(t/2,u/2);let _=Math.round(((n-r)*d+(o-l)*p)/u/c)*c;_=Math.max(h,Math.min(u-h,_)),n=r+_/u*d,o=l+_/u*p,a=_i([n,o],e.angle,t,i,s)||a}}if(!a)return{x:n,y:o,angle:e.angle,measure:null};const r="mi"===this.hass?.config?.unit_system?.length,l=(e,t)=>({x:t[0],y:t[1],text:li(e/this._gridPitch*this._cellCm,r)});return{x:n,y:o,angle:e.angle,measure:{labels:[l(a.sideA,a.midA),l(a.sideB,a.midB)],guide:a.centered?{x:a.wallCenter[0],y:a.wallCenter[1],angle:e.angle}:null}}}_opPointerUp(e,t){if(!this._opDrag||this._opDrag.id!==t.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&this._commitPhysicalGeometry(this._t("history.move_opening"),i.before),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(e,t){"plan"===this._mode&&"resize"===this._tool||(e.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(t))}_saveOpening(){const e=this._openingDialog,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=this._geometrySnapshot(),n=this._spaceH,o=(t.openings||[]).find(t=>t.id===e.id),a={...o||{},id:e.id||"o"+Date.now().toString(36),type:e.type,x:e.x/cv,y:e.y/n,angle:e.angle,length:o&&!e.lengthTouched?o.length:this._cmToUnits(Math.max(20,e.lengthCm))/cv,...e.host?{host:{...e.host}}:{}};if("partition"===a.host?.kind){const e=Rp(o,a)?Ip(a,i.partitions,cv,this._cellCm,this._gridPitch):Tp(a,i.partitions,cv,this._cellCm,this._gridPitch);if(!e.resolved){const t=i.partitions.find(e=>e.id===a.host.id);if("does-not-fit-jamb"===e.reason&&t){const e=Dp(t,this._cellCm,this._gridPitch);this._showToast(this._t("opening.partition_jamb_margin",{distance:li(e/this._gridPitch*this._cellCm,this._imperial)}))}else this._showToast(this._t("opening.partition_orphan"));return}const s=(t.openings||[]).flatMap(e=>{if(!e.host||e.id===a.id)return[];const t=Tp(e,i.partitions,cv,this._cellCm,this._gridPitch).resolved;return t?[t]:[]});if(function(e,t,i=1e-9){const s=e.length/(2*e.axis.length),n=e.t-s,o=e.t+s;return t.some(t=>{if(t.host.id!==e.host.id||t.opening.id===e.opening.id)return!1;const s=t.length/(2*t.axis.length);return Math.max(n,t.t-s)=9){const e=tg(a,t.wall_segments||[]);if(!e)return void this._showToast(this._t("toast.opening_no_wall"));a.host=e}else delete a.host;"passage"===e.type?(delete a.contact,delete a.lock,delete a.invert,delete a.flip_h,delete a.flip_v):(a.contact=e.contact||null,a.lock=("door"===e.type||"gate"===e.type)&&e.lock||null,a.invert=e.invert||void 0,a.flip_h="gate"!==e.type&&e.flipH||void 0,a.flip_v=e.flipV||void 0),t.openings=t.openings||[];const r=t.openings.findIndex(e=>e.id===a.id);r>=0?t.openings[r]=a:t.openings.push(a),this._commitPhysicalGeometry(this._t(e.id?"history.edit_opening":"history.add_opening"),s),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const e=this._openingDialog,t=this._curSpaceCfg;if(!e?.id||!t?.openings)return;const i=this._geometrySnapshot();t.openings=t.openings.filter(t=>t.id!==e.id),this._commitPhysicalGeometry(this._t("history.delete_opening"),i),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const e=[];for(const t of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(t))continue;const i=t.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[t],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&e.push([t,s?.attributes?.friendly_name||t,n?0:1])}return e.sort((e,t)=>e[2]-t[2]||e[1].localeCompare(t[1])).map(([e,t])=>({value:e,label:t}))}_lockCandidates(){return Object.keys(this.hass.states).filter(e=>e.startsWith("lock.")&&this._openingEntityAvailable(e)).map(e=>({value:e,label:this.hass.states[e]?.attributes?.friendly_name||e})).sort((e,t)=>e.label.localeCompare(t.label))}_toggleOpeningEntityPicker(e){const t=this._openingDialog;if(!t)return;const i="contact"===e?!t.contactOpen:!t.lockOpen;this._openingDialog={...t,contactOpen:"contact"===e&&i,lockOpen:"lock"===e&&i}}_filterOpeningEntities(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contactFilter:t}:{...i,lockFilter:t})}_selectOpeningEntity(e,t){const i=this._openingDialog;i&&(this._openingDialog="contact"===e?{...i,contact:t,contactOpen:!1,contactFilter:""}:{...i,lock:t,lockOpen:!1,lockFilter:""})}_mergeClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms,s=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!s?.id)return;const n=s.id;if(!this._mergeSel||this._mergeSel===n)return void(this._mergeSel=this._mergeSel===n?null:n);const o=i.find(e=>e.id===this._mergeSel),a=o?hi(o):null,r=hi(s),l=a&&r?function(e,t){if(!e||!t||e.length<3||t.length<3)return null;const i=si(Ii(e),Ii(t));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(e=>[e[0],e[1]]);return s.length>=3?s:null}(a,r):null;if(!l)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:n,poly:l,pick:"a"},this._mergeSel=null}_commitMerge(){const e=this._mergeDialog,t=this._curSpaceCfg;if(!e||!t||!this._spaceModel())return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===e.pick?e.aId:e.bId,o="a"===e.pick?e.bId:e.aId,a=t.rooms.find(e=>e.id===n);if(!a)return void(this._mergeDialog=null);a.poly=e.poly.map(e=>[e[0]/cv,e[1]/s]),delete a.x,delete a.y,delete a.w,delete a.h,t.rooms=t.rooms.filter(e=>e.id!==o);const r=this._commitPhysicalGeometry(this._t("history.merge_rooms"),i);this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),r&&this._showToast(this._t("toast.rooms_merged",{name:a.name||""}))}_splitClick(e){const t=this._spaceModel();if(!t)return;const i=t.rooms;if(!this._splitSel){const t=[...i].reverse().find(t=>this._pointInRoom(e,t));if(!t?.id)return;return void(this._splitSel={roomId:t.id,pts:[]})}const s=i.find(e=>e.id===this._splitSel.roomId),n=s?hi(s):null;if(!s||!n)return void(this._splitSel=null);const o=.02*this._gridPitch,a=6*this._gridPitch,r=function(e,t){if(!t||t.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[a+g*h,r+g*d]}return s}(r,n,this._gridPitch)||r,c=r&&l&&Math.hypot(r[0]-e[0],r[1]-e[1])<=a?l:null,h=!!c&&bi(c,n,o),d=this._splitSel.pts;if(!d.length)return h?void(this._splitSel={...this._splitSel,pts:[c]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const t=this._snap(e);return wi(t,n,o)?void(this._splitSel={...this._splitSel,pts:[...d,t]}):void this._showToast(this._t("toast.split_pick_inside"))}const p=zi(n,[...d,c],o);if(!p)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,_]=p,m=Ti(u)>=Ti(_)?u:_,g=m===u?_:u;this._pendingSplit={roomId:s.id,mainPoly:m,newPoly:g},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}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)return;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(e)));if("opening"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(e));const t="draw"===this._tool&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!t&&!i)return;const s=this._svgPoint(e);if(t){const t=this._resolvePlanDrawPoint(s,e.shiftKey);return this._planSnapHover={contextKey:t.contextKey,candidate:t.candidate,conflicts:t.conflicts},this._syncPlanSnapConflictMarkers(t.conflicts),this._path.length?void(this._cursorPt=t.point):void this._syncPlanSnapActiveMarker(t.candidate)}this._cursorPt=this._snap(s)}get _openingPreview(){const e=this._openingPreset;if("opening"!==this._tool||!e||!this._cursorPt)return null;const t=this._cursorPt,i=this._openingHoverCandidate;if(i&&Mf(i,[t[0],t[1]],e.revision,this._cfgEpoch))return i;if(this._openingAt(t))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(t);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&(this._wallFaceBatch?this._decideWallFace(!0):this._commitRoom())}_decideWallFace(e){const t=this._wallFaceBatch;if(!t)return;const i=t.candidates[t.index];if(!i)return;if(!e&&(i.existing||i.repair)&&1===t.candidates.length)return this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",void this.requestUpdate();const s=e?{candidate:i,create:!0,name:this._nameSel.trim(),area:this._areaSel||null,settings:this._roomSettingsFromDialog()?JSON.parse(JSON.stringify(this._roomSettingsFromDialog())):null}:{candidate:i,create:!1},n=[...t.decisions,s],o=t.index+1;if(o"number"==typeof e&&Number.isFinite(e)&&e>=0?e:null,n=.02*this._gridPitch,o=this._spaceModel();if(!o)return ul;for(const t of this._planSnapGeometrySnapshot().value.segments){if("room"===t.sourceKind||Ls(e,[t.a[0],t.a[1],t.b[0],t.b[1]])>n)continue;if("partition"===t.sourceKind)return s(o.partitions.find(e=>e.id===t.sourceId)?.cm)??ul;const i=t.sourceId.lastIndexOf(":"),a=i>=0?t.sourceId.slice(0,i):t.sourceId,r=i>=0?Number(t.sourceId.slice(i+1)):-1;return s(o.room_drafts.find(e=>e.id===a)?.segments[r]?.cm)??ul}const a=iu(Math.max(0,t.length-1),i,this._drawWallCm,ul);for(let i=0;i+1[...e]);if(!t?.sourceKey.startsWith("active:"))return i;const s=/:(\d+)$/.exec(t.sourceKey),n=s?Number(s[1])+("b"===t.endpoint?1:0):-1;return n>=0&&nt.key===e.sourceKey),o=s.find(t=>t.key===e.targetSourceKey);if(!n||!o)return!1;const a="a"===e.endpoint?n.a:n.b;return!(Math.hypot(a[0]-e.from[0],a[1]-e.from[1])>i)&&(!(Ls(e.to,[o.a[0],o.a[1],o.b[0],o.b[1]])>i)&&!function(e,t){if(!e.sourceKey.startsWith("static:partition|"))return!1;const i=e.sourceKey.slice(17).split("|")[0];return!!i&&t.some(e=>"partition"===e.host?.kind&&e.host.id===i)}(e,this._curSpaceCfg?.openings||[]))}_applyWallRepair(e,t){const i=this._curSpaceCfg;if(!i||!this._validateWallRepair(e,t.activePath))return!1;const s=t=>{t[0]=e.to[0]/cv,t[1]=e.to[1]/cv};if(e.sourceKey.startsWith("active:")){const n=/:(\d+)$/.exec(e.sourceKey),o=n?Number(n[1])+("b"===e.endpoint?1:0):-1;if(o<0||o>=t.activePath.length)return!1;t.activePath[o]=[...e.to];const a=(i.room_drafts||[]).find(e=>e.id===t.activeDraftId);return a?.points?.[o]&&s(a.points[o]),!0}if(!e.sourceKey.startsWith("static:"))return!1;const[n,o]=e.sourceKey.slice(7).split("|");if("partition"===n){const t=(i.partitions||[]).find(e=>e.id===o);if(!t)return!1;const n=[t.a[0]*cv,t.a[1]*cv],a=[t.b[0]*cv,t.b[1]*cv],r=Math.hypot(n[0]-e.from[0],n[1]-e.from[1])<=2e-4*this._gridPitch?t.a:Math.hypot(a[0]-e.from[0],a[1]-e.from[1])<=2e-4*this._gridPitch?t.b:null;return!!r&&(s(r),!0)}if("draft"===n){const t=o.lastIndexOf(":"),n=t>=0?o.slice(0,t):"",a=t>=0?Number(o.slice(t+1)):-1,r=(i.room_drafts||[]).find(e=>e.id===n);if(!r?.points?.[a]||!r?.points?.[a+1])return!1;const l=[a,a+1].find(t=>Math.hypot(r.points[t][0]*cv-e.from[0],r.points[t][1]*cv-e.from[1])<=2e-4*this._gridPitch);return null!=l&&(s(r.points[l]),!0)}return!1}_applyWallFaceBatch(){const e=this._wallFaceBatch,t=this._curSpaceCfg,i=this._spaceModel();if(!e||!t||!i)return;const s=(e,t)=>{this._showToast(this._t(e,t)),this._roomDialogCancel()},n=e.decisions.filter(e=>e.create),o=n.map(e=>e.candidate.ring),a=i.rooms;for(let e=0;ei.id!==t&&!!hi(i)&&Di(o[e],hi(i)));if(i)return void s("toast.room_overlap",{name:i.name||""})}if((t.rooms||[]).length+n.length>400)return void s("toast.physical_limit");if(n.some(e=>e.candidate.split&&!t.rooms.some(t=>t.id===e.candidate.split.roomId)))return void s("toast.contour_cannot_close");const r=n.map(e=>e.candidate.repair).filter(e=>!!e);if(r.length>1||r[0]&&!this._validateWallRepair(r[0],e.activePath))return void s("toast.wall_repair_changed");const l=this._activePathWithRepair(e.activePath,r[0]),c=2e-4*this._gridPitch,h=new Map;for(let t=0;te.candidate.atomKeys)),t=n.some(e=>e.candidate.consumeAllActive);for(const i of $u(this._wallGraphSources(l),c)){const s=i.sourceKeys.find(e=>h.has(e));!s||t||e.has(i.key)||d.push({a:i.a,b:i.b,cm:h.get(s)})}}else d.push(...su(l,iu(l.length-1,e.activeCms,this._drawWallCm,ul)));if((t.partitions||[]).length+d.length>uv)return void s("toast.physical_limit");const p=n.map(e=>e.candidate.ring.map(()=>"")),u=e.activeDraftId?(t.room_drafts||[]).find(t=>t.id===e.activeDraftId):null,_=this._draftSegmentsForPath(e.activePath,u,e.activeCms),m=(e,t)=>{const i=`${e[0].toFixed(9)},${e[1].toFixed(9)}`,s=`${t[0].toFixed(9)},${t[1].toFixed(9)}`;return i<=s?`${i}|${s}`:`${s}|${i}`};for(let t=0;t<_.length;t++){const i=_[t]?.id;if(!i)continue;const s=e.activePath[t],o=e.activePath[t+1],a=[s[0],s[1],o[0],o[1]],r=[];n.forEach((e,t)=>e.candidate.ring.forEach((i,s)=>{const n=e.candidate.ring[(s+1)%e.candidate.ring.length];Ls(i,a)<=c&&Ls(n,a)<=c&&r.push({kind:"room",owner:t,edge:s,a:i,b:n})})),d.forEach((e,t)=>{Ls(e.a,a)<=c&&Ls(e.b,a)<=c&&r.push({kind:"partition",owner:t,edge:0,a:e.a,b:e.b})});const l=[(s[0]+o[0])/2,(s[1]+o[1])/2];r.sort((e,t)=>{const i=[e.a[0],e.a[1],e.b[0],e.b[1]],n=[t.a[0],t.a[1],t.b[0],t.b[1]];return(Ls(l,i)<=c?0:1)-(Ls(l,n)<=c?0:1)||(Ls(s,i)<=c?0:1)-(Ls(s,n)<=c?0:1)||m(e.a,e.b).localeCompare(m(t.a,t.b))});const h=r[0];if(!h)continue;const u=m(h.a,h.b);for(const e of r)m(e.a,e.b)===u&&("room"===e.kind?p[e.owner][e.edge]=i:d[e.owner].id=i)}const g=this._geometrySnapshot();if(r[0]&&!this._applyWallRepair(r[0],e))return void s("toast.wall_repair_changed");const f=n.some(e=>!!e.candidate.split),v=f?fc(i.rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,cv):null,y=f&&Array.isArray(t.walls)?t.walls:null;for(const e of n){const i=e.candidate.split;if(!i)continue;const n=t.rooms.find(e=>e.id===i.roomId);if(!n)return void s("toast.contour_cannot_close");n.poly=i.mainPoly.map(e=>[e[0]/cv,e[1]/this._spaceH]),delete n.x,delete n.y,delete n.w,delete n.h}const b=Date.now().toString(36),w=[];if(n.forEach((e,i)=>{const s=e.area?this.hass.areas[e.area]?.name:"",n={id:`r${b}-${i}`,name:e.name||s||this._t("room.default_name"),area:e.area||null,poly:e.candidate.ring.map(e=>[e[0]/cv,e[1]/this._spaceH]),...p[i].some(Boolean)?{wall_ids:p[i]}:{},...e.settings?{settings:JSON.parse(JSON.stringify(e.settings))}:{}};t.rooms.push(n),w.push({room:n,decision:e})}),e.activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==e.activeDraftId),t.room_drafts.length||delete t.room_drafts),d.length&&(t.partitions||=[],d.forEach((e,i)=>t.partitions.push({id:e.id||`partition-${b}-${i}`,a:[e.a[0]/cv,e.a[1]/cv],b:[e.b[0]/cv,e.b[1]/cv],cm:e.cm}))),f){const e=this._normalizeWalls(v,this._openCuts());e.length?t.walls=e:y?.length?t.walls=y:delete t.walls}if(w.length){this._cfgEpoch++;const i=this._openCuts(),s=this._spaceModel();if(!s)return;let n=t.walls;for(const{room:t,decision:o}of w){const a=o.candidate.ring,r=this._wallSourceCmAt(a[0],e.activePath,e.activeCms);n=ql(n,s.rooms,t.id,r,this._wallKeyPitch,i,cv);for(const o of gc(s.rooms,n,i,this._wallKeyPitch,this._cellCm,this._gridPitch,cv)){if(o.roomId!==t.id||"outer"!==o.kind)continue;const i=[(o.a[0]+o.b[0])/2,(o.a[1]+o.b[1])/2];n=Hl(n,o.a,o.b,this._wallSourceCmAt(i,e.activePath,e.activeCms),this._wallKeyPitch,cv)}}const o=this._normalizeWalls(n,i);o.length?t.walls=o:delete t.walls}if(!this._commitPhysicalGeometry(this._t("history.wall_face_batch"),g))return;delete this._resumeDraftBySpace[this._space],this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._wallFaceBatch=null,this._roomDialog=!1,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices();const k=new Set(n.map(e=>e.area).filter(e=>!!e));if(k.size){const e={...this._layout};for(const t of this._devices){if(!k.has(t.area||"")||t.space!==this._space)continue;if(this._layout[t.id])continue;const i=this._defPos[t.id];i&&(e[t.id]={s:this._space,x:i.x/cv,y:i.y/cv},this._dirtyPos.add(t.id))}this._layout=e,this._persistLayout()}this._showToast(this._t(n.length?"toast.wall_rooms_saved":"toast.wall_chain_saved",{n:n.length}))}_commitRoom(){const e=this._curSpaceCfg,t=this._spaceModel();if(!e||!t)return;const i=this._geometrySnapshot(),s=this._spaceH,n=!!this._pendingSplit,o=n?fc(t.rooms,e.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,cv):null,a=n&&Array.isArray(e.walls)?e.walls:null;let r;if(this._pendingSplit){const t=e.rooms.find(e=>e.id===this._pendingSplit.roomId);if(!t)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);t.poly=this._pendingSplit.mainPoly.map(e=>[e[0]/cv,e[1]/s]),delete t.x,delete t.y,delete t.w,delete t.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const l=this._areaSel?this.hass.areas[this._areaSel]?.name:"",c={id:"r"+Date.now().toString(36),name:this._nameSel||l||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(e=>[e[0]/cv,e[1]/s]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(!n&&this._activeDraftId){const t=Array.isArray(e.room_drafts)?e.room_drafts.find(e=>e.id===this._activeDraftId):null,i=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),s=this._draftSegmentsForPath(this._path,t,i);s.length===r.length&&s.every(e=>!!e.id)&&(c.wall_ids=s.map(e=>e.id))}if(e.rooms.push(c),!n&&this._activeDraftId&&Array.isArray(e.room_drafts)&&(e.room_drafts=e.room_drafts.filter(e=>e.id!==this._activeDraftId),e.room_drafts.length||delete e.room_drafts),n){const t=this._normalizeWalls(o,this._openCuts());t.length?e.walls=t:a?.length?e.walls=a:delete e.walls}if(!n){const t=iu(r.length,[...this._draftSegmentCms,this._closingWallCm??void 0],this._drawWallCm,ul),i=t[0];if(null!=i){this._cfgEpoch++;const s=this._openCuts(),n=this._spaceModel();if(!n)return;let o=ql(e.walls,n.rooms,c.id,i,this._wallKeyPitch,s,cv);for(const e of gc(n.rooms,o,s,this._wallKeyPitch,this._cellCm,this._gridPitch,cv)){if(e.roomId!==c.id||"outer"!==e.kind)continue;const i=[(e.a[0]+e.b[0])/2,(e.a[1]+e.b[1])/2],s=r.findIndex((e,t)=>{const s=r[(t+1)%r.length];return Ls(i,[e[0],e[1],s[0],s[1]])<=.02*this._gridPitch});s>=0&&(o=Hl(o,e.a,e.b,t[s],this._wallKeyPitch,cv))}o=this._normalizeWalls(o,s),o.length?e.walls=o:delete e.walls}}const h=this._commitPhysicalGeometry(this._t(n?"history.split_room":"history.add_room"),i);this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const d=this._areaSel;if(this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices(),!h)return;let p=0;if(d){const e=cv,t={...this._layout};for(const i of this._devices){if(i.area!==d||i.space!==this._space)continue;if(p++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(t[i.id]={s:this._space,x:s.x/cv,y:s.y/e},this._dirtyPos.add(i.id))}this._layout=t,this._persistLayout()}const u=this._model.find(e=>e.id===this._space)?.rooms.length||0;this._showToast(d?this._t("toast.room_saved",{n:u,added:p}):this._t("toast.room_saved_no_area",{n:u}))}_cancelPath(){this._wallRepairDiagnostic=null,this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._clearPlanSnapHover(),this._roomDialog=!1,this._pendingSplit=null,this._wallFaceBatch=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const e=this._resumeDraftBySpace[this._space];if(!e)return;const t=this._spaceModel()?.room_drafts.find(t=>t.id===e);t?(this._activeDraftId=e,this._path=t.points.map(e=>[...e]),this._draftSegmentCms=this._adoptDraftCms(this._path,t.segments.map(e=>e.cm),t.id),this._clearPlanSnapHover()):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){if(this._roomDialog=!1,this._roomEditId)return this._roomEditId=null,this._nameSel="",void(this._areaSel="");if(this._wallFaceBatch){const e=this._wallFaceBatch;return this._wallFaceBatch=null,this._path=e.activePath.map(e=>[...e]),this._draftSegmentCms=[...e.activeCms],this._activeDraftId=e.activeDraftId,this._nameSel="",this._areaSel="",void this.requestUpdate()}if(this._pendingSplit)return this._pendingSplit=null,void(this._splitSel=null);this._undoPoint()}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||""))}_openMarkerDialog(e){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,e&&this._ackNewDevice(e.id),this._norm)if(e){const t=e.marker,i=Object.prototype.hasOwnProperty.call(t||{},"is_light"),s=Object.prototype.hasOwnProperty.call(t||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(t||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(t||{},"value_badge"),a=Object.prototype.hasOwnProperty.call(t||{},"tap_action"),r=Object.prototype.hasOwnProperty.call(t||{},"toggle_entity"),l=ks(t?.glow_color),c=this._devicePresentation(e,!0).valueBadge,h=Wo(this._planHass,e,this._devices),d=jo(this._planHass,e,h);this._markerDialog={devId:e.id,name:e.name,binding:"virtual"===e.bindingKind?"virtual":e.bindingKind+":"+e.bindingRef,bindingMode:"virtual"===e.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===e.bindingKind&&!!this._fullRegistryHass.entities[e.bindingRef||""]?.device_id,bindingFilter:"",icon:e.marker?.icon||"",autoIcon:e.icon||"",display:Bi(e.marker?.display),rippleColor:Ie(e.marker?.ripple_color,""),rippleSize:Number(e.marker?.ripple_size)>0?Number(e.marker.ripple_size):1.5,size:Number(e.marker?.size)>0?Number(e.marker.size):1,angle:Number(e.marker?.angle)||0,tapAction:vp(e.marker?.tap_action,e.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:a,originalTapAction:e.marker?.tap_action,tapHintAnnouncement:"",toggleEntity:t?.toggle_entity||"",toggleEntityTouched:!1,originalHasToggleEntity:r,originalToggleEntity:t?.toggle_entity,tapTarget:e.marker?.tap_target||"",tapConfirm:!0===e.marker?.tap_confirm,runFilter:"",controls:Zn(e.marker?.binding,e.marker?.controls,e.entities),controlsFilter:"",lightRole:!0===t?.is_light?"always":!1===t?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:t?.is_light,lightEntity:t?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:t?.light_entity,glowMode:null!=l?.bri?"fixed":l?"color":"auto",glowColor:l?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(l?.bri??1))),glowColorDrafted:!!l,glowBrightnessDrafted:null!=l?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!l||null===t?.glow_color),originalGlowColor:l||(null===t?.glow_color?null:void 0),valueBadgeEnabled:o?!0===t?.value_badge?.enabled:!!c,valueBadgeSource:t?.value_badge?.source||c?.source||d,valueBadgePosition:t?.value_badge?.position||c?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:t?.value_badge,useClimateTemp:!0===e.marker?.use_climate_temp,glowRadius:Number(e.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(e.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(e.marker.glow_radius_cm))/100):"",model:e.model||"",link:e.link||"",description:e.description||"",pdfs:[...e.pdfs||[]],room:e.marker?.room_id?e.space+"#@"+e.marker.room_id:e.space&&e.area?e.space+"#"+e.area:"",hideFromPlan:!0===e.marker?.hidden,busy:!1}}else this._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._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,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const e=[];for(const t of Gi)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(t+".")&&this._planEntityAvailable(i)&&e.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+t)});return e.sort((e,t)=>e.sub.localeCompare(t.sub)||e.label.localeCompare(t.label))}_bindingCandidates(){const e=this._planHass,t=wo(this._markers),i=new Set(this._markers.filter(e=>e.removed).map(e=>e.binding)),s=new Set;for(const e of this._devices)e.id!==this._markerDialog?.devId&&("device"===e.bindingKind&&e.bindingRef&&s.add("device:"+e.bindingRef),"entity"===e.bindingKind&&e.bindingRef&&s.add("entity:"+e.bindingRef));const n=new Set;for(const e of this._devices)"device"===e.bindingKind&&e.name&&n.add(e.name.trim()+"|"+(e.area||""));const o=[];for(const t of Object.values(e.devices)){if("service"===t.entry_type)continue;const e="device:"+t.id;if(s.has(e))continue;const a=(t.name_by_user||t.name||t.id).trim();e!==this._markerDialog?.binding&&!i.has(e)&&n.has(a+"|"+(t.area_id||""))||o.push({value:e,label:a,sub:(t.model||this._t("marker.sub_device"))+("Group"===t.model?this._t("marker.sub_z2m_group"):"")})}const a=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,r]of Object.entries(e.entities)){const l="entity:"+n;if(s.has(l))continue;if(ko(e,n,t)&&!i.has(l))continue;const c=a.has(r.platform),h="group"===r.platform;if(!c&&!h)continue;if(r.hidden&&!i.has(l))continue;const d=e.states[n];o.push({value:l,label:r.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===r.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(e=>e.value));for(const[a,r]of Object.entries(e.entities)){const l="entity:"+a;if(s.has(l)||n.has(l)||r.hidden&&!i.has(l))continue;const c=!!r.device_id&&t.devices.has(r.device_id);if(ko(e,a,t)&&!i.has(l)&&!c)continue;const h=e.states[a],d=r.name||h?.attributes?.friendly_name||a,p=r.device_id?e.devices[r.device_id]:null,u=p&&(p.name_by_user||p.name)||"";o.push({value:l,label:d,sub:a.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const r=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=r?o.filter(e=>(e.label+" "+e.sub+" "+e.value).toLowerCase().includes(r)):o;return l.sort((e,t)=>e.label.localeCompare(t.label)),l.slice(0,200)}_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=rh(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:uh([[[...t,t[0]]]])};const s=e.id||`#${i.rooms.indexOf(e)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._resize.preview){const e=Jf(this._cleanFloorCache,n);if(e.hit)return e.value}const o=t.map(e=>e[0]),a=t.map(e=>e[1]),r=[Math.min(...o),Math.min(...a),Math.max(...o),Math.max(...a)],l=this._physicalBodiesR(i).filter(e=>{const t=e.map(e=>e[0]),i=e.map(e=>e[1]);return Math.max(...t)>=r[0]&&Math.min(...t)<=r[2]&&Math.max(...i)>=r[1]&&Math.min(...i)<=r[3]}),c=l.length?ph(t,l):null,h={floor:t,geom:c,path:c?ih(c):"",area:uh(c||[[[...t,t[0]]]])};return this._resize.preview||Xf(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(e){if("virtual"===e)return"mdi:map-marker";const[t,i]=e.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(e),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===t){const e=s.devices?.[i];if(!e)return"mdi:help-circle";const t=o;return t.some(e=>e.startsWith("lock."))?"mdi:lock":bo(s,e.name_by_user||e.name||"",e.model,t,this._iconRules)}if("entity"===t){const e=s.entities?.[i],t=this.hass.states?.[i],n=e?.name||t?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":bo(s,n,"",[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||"").match(/space=([^;]*);\s*opening=([^;]*);\s*fields=([^;]*)/);if(t){const e=this._serverCfg?.spaces?.find(e=>String(e.id)===t[1]),i={contact:"opening.contact_label",lock:"opening.lock_label",invert:"opening.invert",flip_h:"opening.flip_h",flip_v:"opening.flip_v"},s=t[3].split(",").filter(Boolean).map(e=>i[e]?this._t(i[e]):e).join(", ");return this._t("opening.invalid_passage_fields",{room:e?.title||t[1],fields:s})}}if("invalid_partition_opening_jamb_margin"===e.code){const t=String(e.message||e.error||"").match(/margin_cm=([^;]*)/),i=t?Number(t[1]):NaN;if(Number.isFinite(i))return this._t("opening.partition_jamb_margin",{distance:li(i,this._imperial)})}if(e.message)return e.message;if(e.error)return e.error;if(null!=e.code)return this._t("err.code",{code:e.code});try{return JSON.stringify(e)}catch{return String(e)}}_backupErrorText(e){const t=e?.code??e?.error;if("string"==typeof t){const e=`backup.error.${t}`,i=this._t(e);if(i!==e)return i}return this._errText(e)}async _pickMarkerFiles(e){const t=e.target,i=t.files?[...t.files]:[];if(t.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const e of i)try{const t=new FormData;t.append("marker_id",s),t.append("file",e,e.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:t}):await fetch("/api/houseplan/upload",{method:"POST",body:t,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const e={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(e[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||e.name,url:o.url})}catch(t){this._showToast(this._t("toast.file_failed",{name:e.name,err:this._errText(t)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(t=>t.url!==e)})}_markerLightFields(e){const t={};if(e.lightRoleTouched?"always"===e.lightRole?t.is_light=!0:"never"===e.lightRole&&(t.is_light=!1):e.originalHasIsLight&&(t.is_light=e.originalIsLight??null),e.lightEntityTouched?e.lightEntity&&(t.light_entity=e.lightEntity):e.originalHasLightEntity&&(t.light_entity=e.originalLightEntity??null),e.glowTouched){if("auto"!==e.glowMode){const i={c:Ie(e.glowColor,this._fillColors.glow_light.c)};"fixed"===e.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(e.glowBrightness)/100))),t.glow_color=i}}else e.originalHasGlowColor&&(t.glow_color=e.originalGlowColor??null);return t}_markerTapActionFields(e){return e.tapActionTouched?{tap_action:e.tapAction||null}:e.originalHasTapAction?{tap_action:e.originalTapAction??null}:{}}_markerToggleEntityFields(e){return(t={touched:e.toggleEntityTouched,originalHas:e.originalHasToggleEntity,original:e.originalToggleEntity,value:e.toggleEntity}).touched?t.value?{toggle_entity:t.value}:{}:t.originalHas?{toggle_entity:t.original??null}:{};var t}async _saveMarker(){const e=this._markerDialog;if(!e||e.busy)return;const t=this._effectiveMarkerTapAction(e);if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return;if("virtual"===e.binding&&!e.name.trim())return void this._showToast(this._t("toast.virtual_name_required"));if("run"===t&&!e.tapTarget)return void this._showToast(this._t("toast.run_target_required"));if(e.valueBadgeTouched&&e.valueBadgeEnabled&&!e.valueBadgeSource)return void this._showToast(this._t("toast.value_badge_source_required"));if("ha"===e.bindingMode){const t=this._bindingStatus(e.binding),i=e.devId?this._markers.find(t=>t.id===e.devId):null,s=!i||i.binding!==e.binding;if("active"!==t.kind&&s)return void this._showToast(this._t("ha_disabled"===t.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===t.kind&&!0===i?.hidden&&!e.hideFromPlan)return this._markerDialog={...e,hideFromPlan:!0},void this._showToast(this._t("entity"===t.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}const i=this._serverCfg;if(!i)return;const s=i.markers||[],n=qs(e.room);let o=n?.space||null;const a=n?.area||null,r=n?.roomId||null,l=Fi(e.binding,e.devId,()=>"v_"+Date.now().toString(36)),c=e.devId,h=c?this._devices.find(e=>e.id===c):null,d=n?.space||h?.space||null,p=d?this._spaceModelById(d):this._spaceModel();if(!p)return;const u=p.id;"virtual"!==e.binding||o||(o=u),this._markerDialog={...e,busy:!0};try{const n="virtual"===e.binding?[]:s.filter(t=>t.removed&&t.binding===e.binding).map(e=>e.id),d=n.length>0,_=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),m=s.find(e=>e.id===l||e.id===c)?.vacuum||null,g={id:l,vacuum:m,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle?e.angle:null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===t&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:_.length?_:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Yf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:!!e.hideFromPlan};("virtual"===e.binding||e.room)&&(g.space=o,g.area=a,g.room_id=r);const f=h?.marker?.room_id??null,v=!!e.room&&null!=h&&(h.space!==o||h.area!==a||f!==r);let y=!1;const b=e.uploadId||c;if(b&&b!==l&&g.pdfs?.length)try{const e=await this.hass.callWS({type:"houseplan/files/migrate",from_id:b,to_id:l}),t=e?.mapping||{};g.pdfs=function(e,t,i,s){if(!t||!i||t===i)return e;const n="/files/"+t+"/",o="/files/"+i+"/";return e.map(e=>{if(!e.url.includes(n))return e;const t=e.url.split(n)[1]||"",[i,a]=[t.split("?")[0],t.includes("?")?"?"+t.split("?")[1]:""];if(s){const t=s[decodeURIComponent(i)]??s[i];return t?{...e,url:e.url.split(n+i)[0]+o+encodeURIComponent(t)+a}:e}return{...e,url:e.url.split(n).join(o)}})}(g.pdfs,b,l,t),y=Object.keys(t).length>0}catch(e){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(e)}))}i.markers=s,c&&c!==l&&(i.markers=function(e,t,i){if(!t||!i||t===i)return[...e];const s=`marker:${t}`,n=`marker:${i}`;return e.map(e=>{const t=Array.isArray(e.controls)?e.controls.map(e=>e===s?n:e):e.controls,i="derived_marker_state"===e.value_badge?.source?.kind&&e.value_badge.source.ref===s?{...e.value_badge,source:{...e.value_badge.source,ref:n}}:e.value_badge;return t===e.controls&&i===e.value_badge?e:{...e,controls:t,value_badge:i}})}(i.markers,c,l)),c&&c!==l&&"derived_marker_state"===g.value_badge?.source?.kind&&g.value_badge.source.ref===`marker:${c}`&&(g.value_badge.source={kind:"derived_marker_state",ref:`marker:${l}`}),i.markers=i.markers.filter(e=>e.id!==l&&e.id!==c&&("virtual"===g.binding||e.binding!==g.binding)),i.markers.push(g);let w=null;const k=c?this._layout[c]:null,x=k?{s:k.s||h?.space||this._space,x:k.x,y:k.y}:c&&h&&this._defPos[c]?this._normPos(h.space,this._defPos[c].x,this._defPos[c].y):null;if(!d&&x&&x.s===u)l===c&&this._layout[l]&&!v||(w={s:x.s,x:x.x,y:x.y},this._layout={...this._layout,[l]:w});else if(d||!this._layout[l]||v){let e=p.vb[0]+p.vb[2]/2,t=p.vb[1]+p.vb[3]/2;const i=r?p.rooms.find(e=>e.id===r):a?p.rooms.find(e=>e.area===a):void 0;i&&([e,t]=this._roomCenter(i)),w=this._normPos(u,e,t),this._layout={...this._layout,[l]:w}}if(await this._saveConfigNow(),w){const e=hl(w);this._layout={...this._layout,[l]:e},this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:l,pos:e}))}const $=new Set(n);c&&c!==l&&$.add(c),$.delete(l);for(const e of $)delete this._layout[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{});y&&b&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:b}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(e){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _deleteMarker(){const e=this._markerDialog;if(!e||e.busy||!e.devId)return;const t=e.devId?this._devices.find(t=>t.id===e.devId):null;if(!t)return;const i=e.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===t.bindingKind?"virtual":t.bindingKind&&t.bindingRef?`${t.bindingKind}:${t.bindingRef}`:"";if(!o)return;const a=function(e,t,i,s){const n=new Set([t]),o=e.filter(e=>{const o=e.id===t||!s&&e.binding===i;return o&&n.add(e.id),!o});return{markers:s?o:[...o,{id:t,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,t.id,o,"virtual"===t.bindingKind);var r,l;s.markers=(r=a.markers,l=a.cleanupIds,r.map(e=>{if(!Array.isArray(e.controls))return e;const t=e.controls.filter(e=>!("string"==typeof e&&e.startsWith("marker:")&&l.has(e.slice(7))));return t.length===e.controls.length?e:{...e,controls:t.length?t:null}}));const c=a.cleanupIds;this._markerDialog={...e,busy:!0};try{await this._saveConfigNow();for(const e of c){delete this._layout[e],delete this._defPos[e],this._dirtyPos.delete(e),this._sentPos.delete(e);const t=this._activityRt.get(e);t&&clearTimeout(t.timer),this._activityRt.delete(e),this._vacRt.delete(e),delete this._vacSrvTrails[e],await this.hass.callWS({type:"houseplan/layout/delete",device_id:e}).then(e=>this._noteLayoutRev(e)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:e}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:e}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===t.id&&this._closeInfoCard(),this._selId===t.id&&(this._selId=null),this._drag?.id===t.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(e){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}_normPos(e,t,i){return{s:e,x:t/cv,y:i/cv}}_openSpaceDialog(e,t){if(this._serverStorage&&this._serverCfg)if("edit"===e){const i=this._serverCfg.spaces.find(e=>e.id===t);if(!i)return;const s=cs(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?rs(i.settings.custom_fill):null,o="none"===s.fill?{...n||as,a:0}:n;this._spaceDialog={mode:e,spaceId:t,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,zeroWallStyle:v_(i),displayTouched:!0,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Pr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:xh(Number(i.cell_cm)>0?Number(i.cell_cm):5,this._imperial),cellCmTouched:!1,busy:!1}}else this._spaceDialog={mode:e,title:"",planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:{...as,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(e){const t=e.target,i=t.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let e=0;e{const t=new Image;t.onload=()=>e(t.naturalWidth&&t.naturalHeight?t.naturalWidth/t.naturalHeight:1.414),t.onerror=()=>e(1.414),t.src=r});URL.revokeObjectURL(r),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:a,aspect:l,name:i.name}}}_useServerPlan(e){const t=this._spaceDialog;t&&(this._spaceDialog={...t,planUrl:e,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(e))}async _readPlanAspect(e){for(let t=0;t<40;t++){const t=this._display(e);if(t){const i=await new Promise(e=>{const i=new Image;i.onload=()=>e(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>e(0),i.src=t}),s=this._spaceDialog;return s&&s.planUrl===e&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(e=>setTimeout(e,150)),this._spaceDialog?.planUrl!==e)return 0}return 0}async _deleteServerPlan(e){if(confirm(this._t("confirm.delete_plan",{name:e})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:e});const t=this._spaceDialog;t?.saved&&(this._spaceDialog={...t,saved:t.saved.filter(t=>t.name!==e)})}catch(e){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(e)}))}}_renderServerPlans(e){if(e.savedBusy)return W`
${this._t("space.loading")}
`;const t=e.saved||[];if(!t.length)return W`
${this._t("space.no_saved")}
`;return W`
${t.map(t=>W`
@@ -5427,7 +5427,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922
`)} -
`}async _saveSpaceDialog(){const e=this._spaceDialog;if(!e||e.busy||!e.title.trim())return;if("file"===e.source&&!e.planFile&&!e.planUrl)return void this._showToast(this._t("toast.plan_required"));const t="create"===e.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...e,busy:!0};try{const n="create"===e.mode?"s"+Date.now().toString(36):e.spaceId;let o=null;if("file"===e.source&&e.planFile){o={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:n,ext:e.planFile.ext,data:e.planFile.b64})).url,aspect:e.planFile.aspect}}let a=e.savedAspect||null;!o&&"file"===e.source&&e.planUrl&&!a&&this._aspectJob&&(a=await this._aspectJob||null);const r=this._serverCfg;let l;if("create"===e.mode)i=n,s=e.title.trim(),l={id:i,title:s,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]},r.spaces.push(l);else{if(l=r.spaces.find(e=>e.id===n),!l)throw new Error("space "+n+" is gone from the config");l.title=e.title.trim()}o?(l.plan_url=o.url,l.plan_aspect=o.aspect):"file"===e.source&&e.planUrl&&e.planUrl!==l.plan_url&&(l.plan_url=e.planUrl,l.plan_aspect=a),"draw"===e.source&&(l.plan_url=null,l.plan_aspect=null,delete l.plan_x,delete l.plan_y,delete l.plan_scale,delete l.plan_scale_x,delete l.plan_scale_y,delete l.plan_angle),l.settings={...l.settings||{},show_borders:e.showBorders,show_names:e.showNames,hide_decor:e.hideDecor||void 0,hide_openings:e.hideOpenings||void 0,room_color:e.roomColor,room_opacity:e.roomOpacity,bg_color:e.bgColor||void 0,bg_mode:e.bgMode||void 0,north_deg:e.northDeg??void 0,sun_rays:e.sunRays??void 0,fill_mode:e.fillMode,custom_fill:e.customFill||void 0,glow_enabled:e.glowEnabled,temp_min:Number.isFinite(e.tempMin)?Math.min(e.tempMin,e.tempMax):20,temp_max:Number.isFinite(e.tempMax)?Math.max(e.tempMin,e.tempMax):25,show_lqi:e.showLqi,card_font_scale:1!==e.cardFontScale?e.cardFontScale:void 0,label_temp:e.labelTemp,label_hum:e.labelHum,label_lqi:e.labelLqi,label_light:e.labelLight},l.zero_wall_style=e.zeroWallStyle,l.cell_cm=Number.isFinite(e.cellCm)&&e.cellCm>0?Math.max(.1,Math.min(Gf,e.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===e.mode&&this._commitSpace(l.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():t||this._importTotal>0?(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(t&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===e.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===e.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(e){"conflict"!==e?.code&&await this._reloadConfigOnly(!0),this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}var i,s}async _deleteSpace(){const e=this._spaceDialog;if(!e||"edit"!==e.mode)return;const t=this._serverCfg;if(!t)return;const i=t.spaces.find(t=>t.id===e.spaceId),s=ug(t,this._layout||{},e.spaceId||""),n=1===t.spaces.length&&t.spaces[0]?.id===e.spaceId;if(!s.count||n){if(confirm(this._t("confirm.delete_space",{title:i.title}))){this._spaceDialog={...e,deleteBlockers:0,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._persistLayout.pending()&&this._persistLayout.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/space/delete",space_id:e.spaceId,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev}),[i,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(i,s),this._cfgRev=t?.config_rev??this._cfgRev,this._layoutRev=t?.layout_rev??this._layoutRev,this._spaceDialog=null,this._space===e.spaceId&&this._commitSpace(this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]);const i=this._serverCfg;if(this._spaceDialog&&i){const t=ug(i,this._layout||{},e.spaceId||""),s=1===i.spaces.length&&i.spaces[0]?.id===e.spaceId;this._spaceDialog={...this._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}else this._spaceDialog={...e,deleteBlockers:s.count}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(e){throw e?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===e?.code&&await this._reloadConfigOnly(),e}}_startImport(){const e=this._importDialog;if(!e)return;const t=e.floors.filter(e=>e.checked).map(e=>e.name);this._importDialog=null,t.length?(this._importQueue=t,this._importTotal=t.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const e=this._importQueue.shift();void 0!==e&&(this._spaceDialog={mode:"create",title:e,planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._showToast(this._t("import.done")))}_renderImportDialog(){const e=this._importDialog,t=e.floors.filter(e=>e.checked).length;return W``}async _saveSpaceDialog(){const e=this._spaceDialog;if(!e||e.busy||!e.title.trim())return;if("file"===e.source&&!e.planFile&&!e.planUrl)return void this._showToast(this._t("toast.plan_required"));const t="create"===e.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...e,busy:!0};try{const n="create"===e.mode?"s"+Date.now().toString(36):e.spaceId;let o=null;if("file"===e.source&&e.planFile){o={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:n,ext:e.planFile.ext,data:e.planFile.b64})).url,aspect:e.planFile.aspect}}let a=e.savedAspect||null;!o&&"file"===e.source&&e.planUrl&&!a&&this._aspectJob&&(a=await this._aspectJob||null);const r=this._serverCfg;let l;if("create"===e.mode)i=n,s=e.title.trim(),l={id:i,title:s,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]},r.spaces.push(l);else{if(l=r.spaces.find(e=>e.id===n),!l)throw new Error("space "+n+" is gone from the config");l.title=e.title.trim()}o?(l.plan_url=o.url,l.plan_aspect=o.aspect):"file"===e.source&&e.planUrl&&e.planUrl!==l.plan_url&&(l.plan_url=e.planUrl,l.plan_aspect=a),"draw"===e.source&&(l.plan_url=null,l.plan_aspect=null,delete l.plan_x,delete l.plan_y,delete l.plan_scale,delete l.plan_scale_x,delete l.plan_scale_y,delete l.plan_angle),l.settings={...l.settings||{},show_borders:e.showBorders,show_names:e.showNames,hide_decor:e.hideDecor||void 0,hide_openings:e.hideOpenings||void 0,room_color:e.roomColor,room_opacity:e.roomOpacity,bg_color:e.bgColor||void 0,bg_mode:e.bgMode||void 0,north_deg:e.northDeg??void 0,sun_rays:e.sunRays??void 0,fill_mode:e.fillMode,custom_fill:e.customFill||void 0,glow_enabled:e.glowEnabled,temp_min:Number.isFinite(e.tempMin)?Math.min(e.tempMin,e.tempMax):20,temp_max:Number.isFinite(e.tempMax)?Math.max(e.tempMin,e.tempMax):25,show_lqi:e.showLqi,card_font_scale:1!==e.cardFontScale?e.cardFontScale:void 0,label_temp:e.labelTemp,label_hum:e.labelHum,label_lqi:e.labelLqi,label_light:e.labelLight},l.zero_wall_style=e.zeroWallStyle,l.cell_cm=Number.isFinite(e.cellCm)&&e.cellCm>0?Math.max(.1,Math.min(Vf,e.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===e.mode&&this._commitSpace(l.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():t||this._importTotal>0?(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(t&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===e.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===e.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(e){"conflict"!==e?.code&&await this._reloadConfigOnly(!0),this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(e)}))}var i,s}async _deleteSpace(){const e=this._spaceDialog;if(!e||"edit"!==e.mode)return;const t=this._serverCfg;if(!t)return;const i=t.spaces.find(t=>t.id===e.spaceId),s=_g(t,this._layout||{},e.spaceId||""),n=1===t.spaces.length&&t.spaces[0]?.id===e.spaceId;if(!s.count||n){if(confirm(this._t("confirm.delete_space",{title:i.title}))){this._spaceDialog={...e,deleteBlockers:0,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._persistLayout.pending()&&this._persistLayout.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/space/delete",space_id:e.spaceId,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev}),[i,s]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(i,s),this._cfgRev=t?.config_rev??this._cfgRev,this._layoutRev=t?.layout_rev??this._layoutRev,this._spaceDialog=null,this._space===e.spaceId&&this._commitSpace(this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){"conflict"!==t?.code&&"space_in_use"!==t?.code||await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]);const i=this._serverCfg;if(this._spaceDialog&&i){const t=_g(i,this._layout||{},e.spaceId||""),s=1===i.spaces.length&&i.spaces[0]?.id===e.spaceId;this._spaceDialog={...this._spaceDialog,busy:!1,deleteBlockers:s?0:t.count}}this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}else this._spaceDialog={...e,deleteBlockers:s.count}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(e){throw e?.physicalGeometryRolledBack?await this._reloadRejectedPhysicalWrite():"conflict"===e?.code&&await this._reloadConfigOnly(),e}}_startImport(){const e=this._importDialog;if(!e)return;const t=e.floors.filter(e=>e.checked).map(e=>e.name);this._importDialog=null,t.length?(this._importQueue=t,this._importTotal=t.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const e=this._importQueue.shift();void 0!==e&&(this._spaceDialog={mode:"create",title:e,planUrl:null,planFile:null,...Id(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:ns,roomOpacity:os,fillMode:"custom",customFill:null,glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:kh(this._imperial),cellCmInput:xh(kh(this._imperial),this._imperial),cellCmTouched:!1,busy:!1})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._commitSpace(this._serverCfg.spaces[0]?.id||this._space),this._setMode("plan"),this._showToast(this._t("import.done")))}_renderImportDialog(){const e=this._importDialog,t=e.floors.filter(e=>e.checked).length;return W`this._importDialog=null}>
${this._t("import.hint")}
@@ -5481,7 +5481,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${this._t("gs.north_letter")} ${null===e?"—":e+"°"} - `}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,s=t?t.bgColor||"":hs(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||s}_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){const i=new Map((t?.spaces||[]).map(e=>[String(e?.id||""),e]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:qf,checkedAt:(new Date).toISOString(),preflightFingerprint:e.fingerprint,failures:e.failures.map(e=>({spaceId:e.spaceId,displayName:e.displayName,reason:e.reason,detail:e.detail??null,spaceGeometryFingerprint:i.has(e.spaceId)?mg(i.get(e.spaceId)):null}))}}_reportPreflightFailure(e,t){e.ok||e.fingerprint===this._reportedPreflightFingerprint||(this._reportedPreflightFingerprint=e.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(e,t)))}_preflightVersionsDiffer(){const e=this._haIntegrationVersion;return"string"==typeof e&&e.length>0&&e!==qf}async _copyPreflightDiagnostics(){const e=this._alignDialog?.preflight;if(!e||e.ok)return;const t=JSON.stringify(this._preflightDiagnostics(e,this._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(t),this._preflightClipboardFallback=null,this._showToast(this._t("gs.preflight_copied"))}catch{this._preflightClipboardFallback=t}}_checkOptimizeGeometry(e){return wg(e,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)})})}_checkSpacePhysicalGeometry(e,t,i){return function(e,t,i={}){const s=(Array.isArray(e?.spaces)?e.spaces:[]).find(e=>String(e?.id||"")===String(t||"")),n=mg(s);if(!s)return{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:n,ok:!1};const o=wg({...e,spaces:[s]},{...i,fingerprint:()=>n}).spaces[0]||{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:n,ok:"failed"!==o.status}}(e,t,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)}),captureWallGeometry:i?(e,t)=>i(t):void 0})}_optimizeReferenceContext(e){const t=this._haRegistry,i=this._fullRegistryHass,s={},n=(...e)=>{for(const t of e){const e=String(t||"").trim();if(e)return e}return""};for(const[e,i]of Object.entries(t.devices||{}))s[e]=n(i?.name_by_user,i?.name,i?.model);for(const[e,i]of Object.entries(t.entities||{}))s[`lg_${e}`]=n(this.hass?.states?.[e]?.attributes?.friendly_name,i?.name,i?.original_name);for(const e of this._devices)s[e.id]=n(e.name,s[e.id]);for(const e of this._serverCfg?.markers||[]){const t=String(e.binding||"").indexOf(":"),o=t>0?e.binding.slice(0,t):"",a=t>0?e.binding.slice(t+1):"",r="device"===o?i?.devices?.[a]:"entity"===o?i?.entities?.[a]:null;s[e.id]=n(e.name,"device"===o?r?.name_by_user:null,r?.name,"entity"===o?this.hass?.states?.[a]?.attributes?.friendly_name:null,s[e.id])}return{effectiveAreaByMarker:Object.fromEntries(this._devices.filter(e=>!e.virtual&&!!e.area).map(e=>[e.id,e.area])),ownerRoster:{authoritative:t.authoritative,deviceIds:Object.keys(t.devices||{}),entityIds:[...new Set([...Object.keys(t.entities||{}),...Object.keys(this.hass?.states||{})])],names:s},removeLiveMissingPositions:e}}_previewAlignDialog(e){if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[];let i;try{i=pg(this._serverCfg,this._layout||{},this._optimizeReferenceContext(e))}catch(e){return void this._showWallModelMigrationBlocked(e)}const s=i.changed?this._checkOptimizeGeometry(i.config):null;s&&this._reportPreflightFailure(s,i.config);const n=Math.ceil(10*i.report.maxShiftCm)/10,o=t.find(e=>null!=e?.id&&String(e.id)===i.report.maxSpace),a=t.length>1&&o?String(o.title||o.id):"";this._preflightClipboardFallback=null,this._alignDialog={report:i.report,config:i.config,layout:i.layout,cm:n,where:a,preflight:s,changed:i.changed,busy:!1,removeLiveMissingPositions:e}}async _runAlignToGrid(){let e=this._alignDialog;if(!e||e.busy||!this._serverCfg||!e.changed||!e.preflight?.ok)return;const t=Md(e.config);if(e.preflight.fingerprint!==t){const t=this._checkOptimizeGeometry(e.config);if(this._reportPreflightFailure(t,e.config),e={...e,preflight:t},this._alignDialog=e,!t.ok)return}this._clearGeometryGesture(),this._alignDialog={...e,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/plan/optimize",config:e.config,layout:e.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=e.config,this._layout=e.layout,this._geometryHistory.clear(),this._cfgRev=t?.config_rev??this._cfgRev+1,this._layoutRev=t?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!t?.can_undo,this._undoKind=t?.can_undo?"optimize":null,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this._alignDialog=null,this._preflightClipboardFallback=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(e.report.moved),m:String(e.report.migrated+e.report.canonicalized+e.report.coordsCanonicalized+e.report.latticeCoordinatesCanonicalized+e.report.wallsMerged+e.report.spansMerged+e.report.partitionsMerged+e.report.partitionsReconciled+e.report.openingsRehosted+e.report.wallsStraightened),r:String(e.report.spaceRefsRemapped+e.report.roomRefsRemapped+e.report.positionsRemapped+e.report.markersDetached+e.report.orphanRoomLabelsRemoved+e.report.orphanDevicePositionsRemoved+e.report.orphanGroupPositionsRemoved)}))}catch(e){if(this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"wall_model_client_outdated"===e?.code)return void this._showToast(this._t("toast.wall_model_client_outdated"));"conflict"===e?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _undoPlanOptimization(){if(!this._canOptimizeUndo||this._optimizeUndoBusy)return;const e=this._undoKind;this._clearGeometryGesture(),this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,i]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=i?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=i?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._undoKind=null,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("import"===e?"backup.import_undone":"gs.optimize_undone"))}catch(e){this._canOptimizeUndo=!1,this._undoKind=null,this._showToast(this._t("toast.error",{err:this._errText(e)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}async _runBackupExport(){const e=this._backupExportDialog;if(e&&!e.busy){this._backupExportDialog={...e,busy:!0,error:""};try{const t=await this.hass.callWS({type:"houseplan/export/create",kind:e.kind,space_id:"space"===e.kind?this._space:void 0,..."space"===e.kind&&e.planOnly?{plan_only:!0}:{},card_version:qf}),i=new Blob([JSON.stringify(t.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(i),n=document.createElement("a");n.href=s,n.download=t.filename||`houseplan-${e.kind}.json`,n.style.display="none",document.body.append(n),n.click(),n.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0),this._backupExportDialog=null,this._showToast(this._t("backup.export_done"))}catch(e){this._backupExportDialog&&(this._backupExportDialog={...this._backupExportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _pickBackupImport(e){const t=e.target,i=t.files?.[0];if(t.value="",i){this._settingsDialog=null,this._backupImportDialog={filename:i.name,size:i.size,token:"",preview:null,expectedConfigRev:this._cfgRev,expectedLayoutRev:this._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain,this._persistLayout.pending()&&this._persistLayout.flush();const e="/api/houseplan/import/preview?duplicate_policy=skip",t={method:"POST",body:i,headers:{"content-type":"application/json"}},s=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth(e,t):await fetch(e,{...t,headers:{...t.headers,...this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}}),n=await s.json().catch(()=>({}));if(!s.ok||n.error)throw Object.assign(new Error(n.message||n.error||`HTTP ${s.status}`),{code:n.error});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,token:n.token,preview:n.preview,expectedConfigRev:n.expected_config_rev,expectedLayoutRev:n.expected_layout_rev,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _setBackupDuplicatePolicy(e){const t=this._backupImportDialog;if(t&&!t.busy&&t.token&&e!==t.duplicatePolicy){this._backupImportDialog={...t,duplicatePolicy:e,busy:!0,error:""};try{const i=await this.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:e});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,preview:{...t.preview,...i.preview},expectedConfigRev:i.expected_config_rev,expectedLayoutRev:i.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,duplicatePolicy:t.duplicatePolicy,preview:t.preview,confirmMissing:t.confirmMissing,busy:!1,error:this._backupErrorText(e)})}}}async _applyBackupImport(){const e=this._backupImportDialog;if(!e||e.busy||!e.token||!e.preview)return;if(e.preview.confirmation_required&&!e.confirmMissing)return;this._backupImportDialog={...e,busy:!0,error:""};const t=this._space;try{const i=await this.hass.callWS({type:"houseplan/import/apply",token:e.token,expected_config_rev:e.expectedConfigRev,expected_layout_rev:e.expectedLayoutRev,duplicate_policy:e.duplicatePolicy,confirm_missing_content:e.confirmMissing}),[s,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(s,n),this._geometryHistory.clear(),this._dirtyPos.clear(),this._sentPos.clear(),this._defPos={},this._cfgEpoch++,this._modelCache=null,this._frame=null,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotConfigEpoch=-1,this._regSignature="",this._signer.invalidate(this.hass),this._resign(),this._maybeRebuildDevices();const o=this._serverCfg?.spaces||[],a="space"===i.kind&&i.space_id?i.space_id:o.some(e=>e.id===t)?t:o[0]?.id||this._space;this._hasFixedFloor?this._adoptInitialSpace(this._model,!0):this._commitSpace(a),this._backupImportDialog=null,this._cacheSnapshot(),this.requestUpdate();const r="space"===i.kind?e.preview.counts:i.counts;this._showToast(this._t("space"===i.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(i.repaired_target_refs||0)}))}catch(e){if("conflict"===e?.code&&this._backupImportDialog?.token)try{const e=this._backupImportDialog,t=await this.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:e.duplicatePolicy});return void(this._backupImportDialog={...e,preview:{...e.preview,...t.preview},expectedConfigRev:t.expected_config_rev,expectedLayoutRev:t.expected_layout_rev,confirmMissing:!1,busy:!1,error:this._t("backup.revalidated")})}catch(t){e=t}this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}_renderBackupExportDialog(){const e=this._backupExportDialog,t=(this._serverCfg?.spaces||[]).find(e=>e.id===this._space);return W``}_stageBg(e){const t=this._settingsDialog,i=this._spaceDialog,s=t?t.bgColor||"":hs(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":e.bgColor||"")||s}_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){const i=new Map((t?.spaces||[]).map(e=>[String(e?.id||""),e]));return{kind:"houseplan-optimize-preflight",origin:"runtime",cardVersion:Wf,checkedAt:(new Date).toISOString(),preflightFingerprint:e.fingerprint,failures:e.failures.map(e=>({spaceId:e.spaceId,displayName:e.displayName,reason:e.reason,detail:e.detail??null,spaceGeometryFingerprint:i.has(e.spaceId)?gg(i.get(e.spaceId)):null}))}}_reportPreflightFailure(e,t){e.ok||e.fingerprint===this._reportedPreflightFingerprint||(this._reportedPreflightFingerprint=e.fingerprint,console.warn("[houseplan] optimize preflight failed",this._preflightDiagnostics(e,t)))}_preflightVersionsDiffer(){const e=this._haIntegrationVersion;return"string"==typeof e&&e.length>0&&e!==Wf}async _copyPreflightDiagnostics(){const e=this._alignDialog?.preflight;if(!e||e.ok)return;const t=JSON.stringify(this._preflightDiagnostics(e,this._alignDialog?.config??null),null,2);try{await navigator.clipboard.writeText(t),this._preflightClipboardFallback=null,this._showToast(this._t("gs.preflight_copied"))}catch{this._preflightClipboardFallback=t}}_checkOptimizeGeometry(e){return kg(e,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)})})}_checkSpacePhysicalGeometry(e,t,i){return function(e,t,i={}){const s=(Array.isArray(e?.spaces)?e.spaces:[]).find(e=>String(e?.id||"")===String(t||"")),n=gg(s);if(!s)return{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception",fingerprint:n,ok:!1};const o=kg({...e,spaces:[s]},{...i,fingerprint:()=>n}).spaces[0]||{spaceId:String(t||""),displayName:"",status:"failed",reason:"prepare-exception"};return{...o,fingerprint:n,ok:"failed"!==o.status}}(e,t,{fallbackSpaceName:e=>this._t("gs.align_preflight_space",{n:String(e)}),captureWallGeometry:i?(e,t)=>i(t):void 0})}_optimizeReferenceContext(e){const t=this._haRegistry,i=this._fullRegistryHass,s={},n=(...e)=>{for(const t of e){const e=String(t||"").trim();if(e)return e}return""};for(const[e,i]of Object.entries(t.devices||{}))s[e]=n(i?.name_by_user,i?.name,i?.model);for(const[e,i]of Object.entries(t.entities||{}))s[`lg_${e}`]=n(this.hass?.states?.[e]?.attributes?.friendly_name,i?.name,i?.original_name);for(const e of this._devices)s[e.id]=n(e.name,s[e.id]);for(const e of this._serverCfg?.markers||[]){const t=String(e.binding||"").indexOf(":"),o=t>0?e.binding.slice(0,t):"",a=t>0?e.binding.slice(t+1):"",r="device"===o?i?.devices?.[a]:"entity"===o?i?.entities?.[a]:null;s[e.id]=n(e.name,"device"===o?r?.name_by_user:null,r?.name,"entity"===o?this.hass?.states?.[a]?.attributes?.friendly_name:null,s[e.id])}return{effectiveAreaByMarker:Object.fromEntries(this._devices.filter(e=>!e.virtual&&!!e.area).map(e=>[e.id,e.area])),ownerRoster:{authoritative:t.authoritative,deviceIds:Object.keys(t.devices||{}),entityIds:[...new Set([...Object.keys(t.entities||{}),...Object.keys(this.hass?.states||{})])],names:s},removeLiveMissingPositions:e}}_previewAlignDialog(e){if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[];let i;try{i=ug(this._serverCfg,this._layout||{},this._optimizeReferenceContext(e))}catch(e){return void this._showWallModelMigrationBlocked(e)}const s=i.changed?this._checkOptimizeGeometry(i.config):null;s&&this._reportPreflightFailure(s,i.config);const n=Math.ceil(10*i.report.maxShiftCm)/10,o=t.find(e=>null!=e?.id&&String(e.id)===i.report.maxSpace),a=t.length>1&&o?String(o.title||o.id):"";this._preflightClipboardFallback=null,this._alignDialog={report:i.report,config:i.config,layout:i.layout,cm:n,where:a,preflight:s,changed:i.changed,busy:!1,removeLiveMissingPositions:e}}async _runAlignToGrid(){let e=this._alignDialog;if(!e||e.busy||!this._serverCfg||!e.changed||!e.preflight?.ok)return;const t=Md(e.config);if(e.preflight.fingerprint!==t){const t=this._checkOptimizeGeometry(e.config);if(this._reportPreflightFailure(t,e.config),e={...e,preflight:t},this._alignDialog=e,!t.ok)return}this._clearGeometryGesture(),this._alignDialog={...e,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const t=await this.hass.callWS({type:"houseplan/plan/optimize",config:e.config,layout:e.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=e.config,this._layout=e.layout,this._geometryHistory.clear(),this._cfgRev=t?.config_rev??this._cfgRev+1,this._layoutRev=t?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!t?.can_undo,this._undoKind=t?.can_undo?"optimize":null,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this._alignDialog=null,this._preflightClipboardFallback=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(e.report.moved),m:String(e.report.migrated+e.report.canonicalized+e.report.coordsCanonicalized+e.report.latticeCoordinatesCanonicalized+e.report.wallsMerged+e.report.spansMerged+e.report.partitionsMerged+e.report.partitionsReconciled+e.report.openingsRehosted+e.report.wallsStraightened),r:String(e.report.spaceRefsRemapped+e.report.roomRefsRemapped+e.report.positionsRemapped+e.report.markersDetached+e.report.orphanRoomLabelsRemoved+e.report.orphanDevicePositionsRemoved+e.report.orphanGroupPositionsRemoved)}))}catch(e){if(this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"wall_model_client_outdated"===e?.code)return void this._showToast(this._t("toast.wall_model_client_outdated"));"conflict"===e?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(e)}))}}async _undoPlanOptimization(){if(!this._canOptimizeUndo||this._optimizeUndoBusy)return;const e=this._undoKind;this._clearGeometryGesture(),this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,i]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=i?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=i?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._undoKind=null,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("import"===e?"backup.import_undone":"gs.optimize_undone"))}catch(e){this._canOptimizeUndo=!1,this._undoKind=null,this._showToast(this._t("toast.error",{err:this._errText(e)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}async _runBackupExport(){const e=this._backupExportDialog;if(e&&!e.busy){this._backupExportDialog={...e,busy:!0,error:""};try{const t=await this.hass.callWS({type:"houseplan/export/create",kind:e.kind,space_id:"space"===e.kind?this._space:void 0,..."space"===e.kind&&e.planOnly?{plan_only:!0}:{},card_version:Wf}),i=new Blob([JSON.stringify(t.document,null,2)+"\n"],{type:"application/json;charset=utf-8"}),s=URL.createObjectURL(i),n=document.createElement("a");n.href=s,n.download=t.filename||`houseplan-${e.kind}.json`,n.style.display="none",document.body.append(n),n.click(),n.remove(),window.setTimeout(()=>URL.revokeObjectURL(s),0),this._backupExportDialog=null,this._showToast(this._t("backup.export_done"))}catch(e){this._backupExportDialog&&(this._backupExportDialog={...this._backupExportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _pickBackupImport(e){const t=e.target,i=t.files?.[0];if(t.value="",i){this._settingsDialog=null,this._backupImportDialog={filename:i.name,size:i.size,token:"",preview:null,expectedConfigRev:this._cfgRev,expectedLayoutRev:this._layoutRev,duplicatePolicy:"skip",confirmMissing:!1,busy:!0,error:""};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain,this._persistLayout.pending()&&this._persistLayout.flush();const e="/api/houseplan/import/preview?duplicate_policy=skip",t={method:"POST",body:i,headers:{"content-type":"application/json"}},s=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth(e,t):await fetch(e,{...t,headers:{...t.headers,...this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}}),n=await s.json().catch(()=>({}));if(!s.ok||n.error)throw Object.assign(new Error(n.message||n.error||`HTTP ${s.status}`),{code:n.error});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,token:n.token,preview:n.preview,expectedConfigRev:n.expected_config_rev,expectedLayoutRev:n.expected_layout_rev,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}}async _setBackupDuplicatePolicy(e){const t=this._backupImportDialog;if(t&&!t.busy&&t.token&&e!==t.duplicatePolicy){this._backupImportDialog={...t,duplicatePolicy:e,busy:!0,error:""};try{const i=await this.hass.callWS({type:"houseplan/import/revalidate",token:t.token,duplicate_policy:e});this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,preview:{...t.preview,...i.preview},expectedConfigRev:i.expected_config_rev,expectedLayoutRev:i.expected_layout_rev,confirmMissing:!1,busy:!1})}catch(e){this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,duplicatePolicy:t.duplicatePolicy,preview:t.preview,confirmMissing:t.confirmMissing,busy:!1,error:this._backupErrorText(e)})}}}async _applyBackupImport(){const e=this._backupImportDialog;if(!e||e.busy||!e.token||!e.preview)return;if(e.preview.confirmation_required&&!e.confirmMissing)return;this._backupImportDialog={...e,busy:!0,error:""};const t=this._space;try{const i=await this.hass.callWS({type:"houseplan/import/apply",token:e.token,expected_config_rev:e.expectedConfigRev,expected_layout_rev:e.expectedLayoutRev,duplicate_policy:e.duplicatePolicy,confirm_missing_content:e.confirmMissing}),[s,n]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._adoptStructuralResponses(s,n),this._geometryHistory.clear(),this._dirtyPos.clear(),this._sentPos.clear(),this._defPos={},this._cfgEpoch++,this._modelCache=null,this._frame=null,this._visibleDeviceSnapshot=null,this._candidateDeviceSnapshot=null,this._stagedDeviceSnapshotToken=-1,this._capturedSnapshotConfigEpoch=-1,this._regSignature="",this._signer.invalidate(this.hass),this._resign(),this._maybeRebuildDevices();const o=this._serverCfg?.spaces||[],a="space"===i.kind&&i.space_id?i.space_id:o.some(e=>e.id===t)?t:o[0]?.id||this._space;this._hasFixedFloor?this._adoptInitialSpace(this._model,!0):this._commitSpace(a),this._backupImportDialog=null,this._cacheSnapshot(),this.requestUpdate();const r="space"===i.kind?e.preview.counts:i.counts;this._showToast(this._t("space"===i.kind?"backup.space_done":"backup.full_done",{spaces:String(r?.spaces||0),rooms:String(r?.rooms||0),markers:String(r?.markers||0),refs:String(i.repaired_target_refs||0)}))}catch(e){if("conflict"===e?.code&&this._backupImportDialog?.token)try{const e=this._backupImportDialog,t=await this.hass.callWS({type:"houseplan/import/revalidate",token:e.token,duplicate_policy:e.duplicatePolicy});return void(this._backupImportDialog={...e,preview:{...e.preview,...t.preview},expectedConfigRev:t.expected_config_rev,expectedLayoutRev:t.expected_layout_rev,confirmMissing:!1,busy:!1,error:this._t("backup.revalidated")})}catch(t){e=t}this._backupImportDialog&&(this._backupImportDialog={...this._backupImportDialog,busy:!1,error:this._backupErrorText(e)})}}_renderBackupExportDialog(){const e=this._backupExportDialog,t=(this._serverCfg?.spaces||[]).find(e=>e.id===this._space);return W`this._backupExportDialog=null}>
${this._t("backup.export_hint")}
@@ -5587,12 +5587,12 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 .pickerLabels=${this._colorPickerLabels} .color=${i.c} .opacity=${i.a} .showOpacity=${!0} @hp-color-opacity-change=${t=>{this._setFillColor(e,{c:t.detail.color,a:t.detail.opacity})}}> -
`}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){let i=this._glowRenderedSources.get(e);if(t){const t=this._glowFadeTimers.get(e);if(null!=t&&(clearTimeout(t),this._glowFadeTimers.delete(e)),null==i){this._suspendGlowFeatherForTransition(),i=++this._glowSourceSeq,this._glowRenderedSources.set(e,i),this._glowEnteringSources.add(e);const t=requestAnimationFrame(()=>{this._glowEnterRafs.get(e)===t&&(this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),this.isConnected&&this.requestUpdate())});this._glowEnterRafs.set(e,t)}return{domId:i,entering:this._glowEnteringSources.has(e),leaving:!1}}if(null==i)return null;const s=this._glowEnterRafs.get(e);if(null!=s&&cancelAnimationFrame(s),this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),!this._glowFadeTimers.has(e)){this._suspendGlowFeatherForTransition();const t=window.setTimeout(()=>{this._glowFadeTimers.get(e)===t&&(this._glowFadeTimers.delete(e),this._glowRenderedSources.delete(e),this._glowLastAppearance.delete(e),this.isConnected&&this.requestUpdate())},534);this._glowFadeTimers.set(e,t)}return{domId:i,entering:!1,leaving:!0}}_suspendGlowFeatherForTransition(){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;this._glowFeatherSuspendUntil=Math.max(this._glowFeatherSuspendUntil,Date.now()+500),clearTimeout(this._glowFeatherResumeTimer);const e=Math.max(0,this._glowFeatherSuspendUntil-Date.now())+17,t=()=>{this._glowFeatherResumeTimer=0,Date.now()=128){const e=this._glowGeometryWarnings.values().next().value;e&&this._glowGeometryWarnings.delete(e)}this._glowGeometryWarnings.add(n),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${e}, room ${i}, phase ${s}`)}}_lightBarriers(e,t){const i=Md([this._curSpaceCfg,this._cellCm,this._gridPitch]),s=`${e.id}|${i}`,n=Yf(this._lightBarrierPool,s);if(n.hit)return this._lightBarrierCache=n.value,n.value.value;const o=this._zeroWalls(),a=this._openCuts(),r=[...o.transmissive],l=Math.max(this._cmToUnits(10),.5*this._gridPitch),c=e=>t.some(t=>this._pointInRoom(e,t.r)),h=this._openingsR.filter(e=>{if("door"!==(t=String(e.type))&&"gate"!==t&&"passage"!==t)return!1;var t;const i=e.angle*Math.PI/180,s=-Math.sin(i),n=Math.cos(i);return c([e.rx+s*l,e.ry+n*l])&&c([e.rx-s*l,e.ry-n*l])}),d=this._roomWallOpeningInputs(h,e);for(const e of d){const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;r.push([e.x-i,e.y-s,e.x+i,e.y+s])}const p=new Set(h.filter(e=>e.partitionHost).map(e=>e.id)),u=[e.id,this._cfgEpoch,this._cellCm,this._gridPitch,[...p].sort().join(",")].join("|");this._lightPhysicalBodiesCache?.key!==u&&(this._lightPhysicalBodiesCache={key:u,all:rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e,e=>p.has(e.id))).all});const _=this._lightPhysicalBodiesCache.all,m=this._spaceWalls,g=.02*this._gridPitch,f=[],v=this._wallUnionGeometry(),y=v?.sourceFingerprint,b=v&&y===i?function(e,t=[],i=[],s={}){if("failed-core"===e.status||"not-applicable"===e.status)return null;const n=e.roomComponents?.length?e.roomComponents:$c(e.roomGeom)?[{id:"room-primary",geom:e.roomGeom}]:[];let o=n[0]?.geom||null;const a=n.slice(1).map(e=>({...e}));try{for(const i of t){if(!(i.length>0&&e.openingIndex))continue;const t=jc(e.openingIndex,i,!0);if(!t.negative&&!t.positive)continue;const s=i.angle*Math.PI/180,n=Math.cos(s),r=Math.sin(s),l=-r,c=n,h=i.length/2,d=e.openingPadUnits??1.25*Math.max(e.depthUnits,1),p=xc([[i.x-n*h-l*d,i.y-r*h-c*d],[i.x+n*h-l*d,i.y+r*h-c*d],[i.x+n*h+l*d,i.y+r*h+c*d],[i.x-n*h+l*d,i.y-r*h+c*d]]);o&&(o=oi(o,p));for(const e of a)e.geom=oi(e.geom,p)}const n=[...a];let r=a.length;const l=s.mergeExtra||((e,t)=>e?si(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(Ti(t))<=1e-9){r++;continue}const s=[xc(t)];try{const t=l(o,s,e);if(!$c(t))throw new Error("invalid extra union");o=t}catch{r++,$c(s)&&n.push({id:`policy-extra-${e}`,geom:s})}}n.sort((e,t)=>Fc(e.geom).localeCompare(Fc(t.geom)));const c=o||[];return{status:r?"degraded-extra":"ok",geom:c,components:[...$c(c)?[{id:"primary",geom:c}]:[],...n.map((e,t)=>({...e,id:`isolated-${t}`}))]}}catch{return null}}(v,d,_):null,w=b||(m.length||_.length?Ec(e.rooms,m,a,d,this._wallKeyPitch,this._cellCm,this._gridPitch,lv,_):null);if(!w||"ok"!==w.status&&"degraded-extra"!==w.status)for(const e of _)f.push(...Lu(e));else for(const e of w.components)for(const t of _h(e.geom))f.push(...Lu(t));for(const{poly:e}of t)for(const t of r.length?Ps(e,r,g):Lu(e))f.push(t);for(const e of o.barriers)f.push(e);const k={occluders:Hu(f),floor:t.map(e=>e.poly),fingerprint:i,masonryGeometry:!w||"ok"!==w.status&&"degraded-extra"!==w.status?[]:w.components.flatMap(e=>e.geom),opaqueBodies:_},x={key:s,value:k};return Jf(this._lightBarrierPool,s,x,8),this._lightBarrierCache=x,k}_renderGlowLayer(e,t){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,n=e.rooms.map(e=>({r:e,poly:hi(e)})).filter(e=>!!e.poly),o=n.filter(({r:e})=>Fs(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const{occluders:a,floor:r,fingerprint:l,masonryGeometry:c,opaqueBodies:h}=this._lightBarriers(e,n),d=ho(this._renderPlanHass,this._renderDevices,null,this._virtualLights).filter(t=>t.device.space===e.id),p=new Map;for(const e of d){if(!e.device.id)continue;const t=p.get(e.device.id)||[];t.push(e),p.set(e.device.id,t)}const u=[],_=new Set;for(const t of this._renderDevices){if(t.space!==e.id)continue;const o=po(p.get(t.id)||[]);if(!o)continue;const d=`${e.id}|${t.id}`;_.add(d);const m=$s(o.passive?{state:o.on?"on":"off",attributes:{}}:this._renderPlanHass.states[o.eid],t.marker?.glow_color,i.glow_light.c),g=Number(t.marker?.glow_radius_cm),f=Number.isFinite(g)&&g>0?g/this._cellCm*this._gridPitch:s,v=this._pos(t);if(fh([v.x,v.y],c,h)){this._forgetGlowSource(d);continue}const y=this._glowTransition(d,!!m);if(!y)continue;m&&this._glowLastAppearance.set(d,{c:m.c,alpha:Ss(m.bri,i.glow_light.a)});const b=this._glowLastAppearance.get(d);if(!b)continue;let w=null;const k=`${e.id}|${l}|${v.x.toFixed(4)},${v.y.toFixed(4)}|${f.toFixed(4)}`,x=Yf(this._glowClipCache,k);if(x.hit)w=x.value;else{const t=Wu([v.x,v.y],f,a,96);w={lit:t.length>=3?dh([t],r,{onBoundsFailure:({boundIndex:t,phase:i})=>{const s=n[t]?.r;this._warnGlowGeometryFallback(e.id,l,s?.id||`#${t}`,i)}}):[]},Jf(this._glowClipCache,k,w,256)}u.push({key:d,sourceEid:o.eid,domId:y.domId,entering:y.entering,leaving:y.leaving,pos:v,c:b.c,alpha:b.alpha,geometry:w,r:f})}const m=`${e.id}|`;for(const e of this._glowRenderedSources.keys())e.startsWith(m)&&!_.has(e)&&this._forgetGlowSource(e);if(!u.length)return j``;const g=this._spaceWalls,f=o.length===n.length?[]:this._openCuts(),v=this._wallUnionGeometry()?.roomGeom,y=o.length===n.length?null:o.map(({r:t,poly:i})=>{const s=g.length&&t.id&&this._innerRoomContour(e,t.id,f,v)||i,o=this._cleanFloor(t,s,e).path,a=Pi(s,n.filter(e=>e.r!==t).map(e=>e.poly)),r=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||r(s),...a.map(r)].join(" ")}),b=this._viewOr(this._baseVb()),w=this._stageEl?.clientWidth&&b.w?this._stageEl.clientWidth/b.w:1,k=1/(w>0?w:1),x=!this._pinchStart&&!this._panStart&&Date.now()>=this._glowFeatherSuspendUntil;(null==this._glowFeatherUnits||x)&&(this._glowFeatherUnits=k);const $=this._glowFeatherUnits??k,S=4*$,M=u.reduce((e,t)=>({x:Math.min(e.x,t.pos.x-t.r-S),y:Math.min(e.y,t.pos.y-t.r-S),maxX:Math.max(e.maxX,t.pos.x+t.r+S),maxY:Math.max(e.maxY,t.pos.y+t.r+S),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});return M.w=M.maxX-M.x,M.h=M.maxY-M.y,j` +
`}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){let i=this._glowRenderedSources.get(e);if(t){const t=this._glowFadeTimers.get(e);if(null!=t&&(clearTimeout(t),this._glowFadeTimers.delete(e)),null==i){this._suspendGlowFeatherForTransition(),i=++this._glowSourceSeq,this._glowRenderedSources.set(e,i),this._glowEnteringSources.add(e);const t=requestAnimationFrame(()=>{this._glowEnterRafs.get(e)===t&&(this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),this.isConnected&&this.requestUpdate())});this._glowEnterRafs.set(e,t)}return{domId:i,entering:this._glowEnteringSources.has(e),leaving:!1}}if(null==i)return null;const s=this._glowEnterRafs.get(e);if(null!=s&&cancelAnimationFrame(s),this._glowEnterRafs.delete(e),this._glowEnteringSources.delete(e),!this._glowFadeTimers.has(e)){this._suspendGlowFeatherForTransition();const t=window.setTimeout(()=>{this._glowFadeTimers.get(e)===t&&(this._glowFadeTimers.delete(e),this._glowRenderedSources.delete(e),this._glowLastAppearance.delete(e),this.isConnected&&this.requestUpdate())},534);this._glowFadeTimers.set(e,t)}return{domId:i,entering:!1,leaving:!0}}_suspendGlowFeatherForTransition(){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;this._glowFeatherSuspendUntil=Math.max(this._glowFeatherSuspendUntil,Date.now()+500),clearTimeout(this._glowFeatherResumeTimer);const e=Math.max(0,this._glowFeatherSuspendUntil-Date.now())+17,t=()=>{this._glowFeatherResumeTimer=0,Date.now()=128){const e=this._glowGeometryWarnings.values().next().value;e&&this._glowGeometryWarnings.delete(e)}this._glowGeometryWarnings.add(n),console.warn(`HOUSEPLAN GLOW GEOMETRY FALLBACK: #218, space ${e}, room ${i}, phase ${s}`)}}_lightBarriers(e,t){const i=Md([this._curSpaceCfg,this._cellCm,this._gridPitch]),s=`${e.id}|${i}`,n=Jf(this._lightBarrierPool,s);if(n.hit)return this._lightBarrierCache=n.value,n.value.value;const o=this._zeroWalls(),a=this._openCuts(),r=[...o.transmissive],l=Math.max(this._cmToUnits(10),.5*this._gridPitch),c=e=>t.some(t=>this._pointInRoom(e,t.r)),h=this._openingsR.filter(e=>{if("door"!==(t=String(e.type))&&"gate"!==t&&"passage"!==t)return!1;var t;const i=e.angle*Math.PI/180,s=-Math.sin(i),n=Math.cos(i);return c([e.rx+s*l,e.ry+n*l])&&c([e.rx-s*l,e.ry-n*l])}),d=this._roomWallOpeningInputs(h,e);for(const e of d){const t=e.angle*Math.PI/180,i=Math.cos(t)*e.length/2,s=Math.sin(t)*e.length/2;r.push([e.x-i,e.y-s,e.x+i,e.y+s])}const p=new Set(h.filter(e=>e.partitionHost).map(e=>e.id)),u=[e.id,this._cfgEpoch,this._cellCm,this._gridPitch,[...p].sort().join(",")].join("|");this._lightPhysicalBodiesCache?.key!==u&&(this._lightPhysicalBodiesCache={key:u,all:rh(e,this._cellCm,this._gridPitch,2e-4*this._gridPitch,this._partitionOpeningCuts(e,e=>p.has(e.id))).all});const _=this._lightPhysicalBodiesCache.all,m=this._spaceWalls,g=.02*this._gridPitch,f=[],v=this._wallUnionGeometry(),y=v?.sourceFingerprint,b=v&&y===i?function(e,t=[],i=[],s={}){if("failed-core"===e.status||"not-applicable"===e.status)return null;const n=e.roomComponents?.length?e.roomComponents:$c(e.roomGeom)?[{id:"room-primary",geom:e.roomGeom}]:[];let o=n[0]?.geom||null;const a=n.slice(1).map(e=>({...e}));try{for(const i of t){if(!(i.length>0&&e.openingIndex))continue;const t=jc(e.openingIndex,i,!0);if(!t.negative&&!t.positive)continue;const s=i.angle*Math.PI/180,n=Math.cos(s),r=Math.sin(s),l=-r,c=n,h=i.length/2,d=e.openingPadUnits??1.25*Math.max(e.depthUnits,1),p=xc([[i.x-n*h-l*d,i.y-r*h-c*d],[i.x+n*h-l*d,i.y+r*h-c*d],[i.x+n*h+l*d,i.y+r*h+c*d],[i.x-n*h+l*d,i.y-r*h+c*d]]);o&&(o=oi(o,p));for(const e of a)e.geom=oi(e.geom,p)}const n=[...a];let r=a.length;const l=s.mergeExtra||((e,t)=>e?si(e,t):t);for(let e=0;ee.length>=2&&Number.isFinite(e[0])&&Number.isFinite(e[1]))||Math.abs(Ti(t))<=1e-9){r++;continue}const s=[xc(t)];try{const t=l(o,s,e);if(!$c(t))throw new Error("invalid extra union");o=t}catch{r++,$c(s)&&n.push({id:`policy-extra-${e}`,geom:s})}}n.sort((e,t)=>Fc(e.geom).localeCompare(Fc(t.geom)));const c=o||[];return{status:r?"degraded-extra":"ok",geom:c,components:[...$c(c)?[{id:"primary",geom:c}]:[],...n.map((e,t)=>({...e,id:`isolated-${t}`}))]}}catch{return null}}(v,d,_):null,w=b||(m.length||_.length?Ec(e.rooms,m,a,d,this._wallKeyPitch,this._cellCm,this._gridPitch,cv,_):null);if(!w||"ok"!==w.status&&"degraded-extra"!==w.status)for(const e of _)f.push(...Hu(e));else for(const e of w.components)for(const t of _h(e.geom))f.push(...Hu(t));for(const{poly:e}of t)for(const t of r.length?Ps(e,r,g):Hu(e))f.push(t);for(const e of o.barriers)f.push(e);const k={occluders:Lu(f),floor:t.map(e=>e.poly),fingerprint:i,masonryGeometry:!w||"ok"!==w.status&&"degraded-extra"!==w.status?[]:w.components.flatMap(e=>e.geom),opaqueBodies:_},x={key:s,value:k};return Xf(this._lightBarrierPool,s,x,8),this._lightBarrierCache=x,k}_renderGlowLayer(e,t){const i=this._fillColors,s=this._glowRadiusCm/this._cellCm*this._gridPitch,n=e.rooms.map(e=>({r:e,poly:hi(e)})).filter(e=>!!e.poly),o=n.filter(({r:e})=>Fs(t.glow,e));if(!o.length)return this._forgetGlowSpace(e.id),j``;const{occluders:a,floor:r,fingerprint:l,masonryGeometry:c,opaqueBodies:h}=this._lightBarriers(e,n),d=ho(this._renderPlanHass,this._renderDevices,null,this._virtualLights).filter(t=>t.device.space===e.id),p=new Map;for(const e of d){if(!e.device.id)continue;const t=p.get(e.device.id)||[];t.push(e),p.set(e.device.id,t)}const u=[],_=new Set;for(const t of this._renderDevices){if(t.space!==e.id)continue;const o=po(p.get(t.id)||[]);if(!o)continue;const d=`${e.id}|${t.id}`;_.add(d);const m=$s(o.passive?{state:o.on?"on":"off",attributes:{}}:this._renderPlanHass.states[o.eid],t.marker?.glow_color,i.glow_light.c),g=Number(t.marker?.glow_radius_cm),f=Number.isFinite(g)&&g>0?g/this._cellCm*this._gridPitch:s,v=this._pos(t);if(fh([v.x,v.y],c,h)){this._forgetGlowSource(d);continue}const y=this._glowTransition(d,!!m);if(!y)continue;m&&this._glowLastAppearance.set(d,{c:m.c,alpha:Ss(m.bri,i.glow_light.a)});const b=this._glowLastAppearance.get(d);if(!b)continue;let w=null;const k=`${e.id}|${l}|${v.x.toFixed(4)},${v.y.toFixed(4)}|${f.toFixed(4)}`,x=Jf(this._glowClipCache,k);if(x.hit)w=x.value;else{const t=Wu([v.x,v.y],f,a,96);w={lit:t.length>=3?dh([t],r,{onBoundsFailure:({boundIndex:t,phase:i})=>{const s=n[t]?.r;this._warnGlowGeometryFallback(e.id,l,s?.id||`#${t}`,i)}}):[]},Xf(this._glowClipCache,k,w,256)}u.push({key:d,sourceEid:o.eid,domId:y.domId,entering:y.entering,leaving:y.leaving,pos:v,c:b.c,alpha:b.alpha,geometry:w,r:f})}const m=`${e.id}|`;for(const e of this._glowRenderedSources.keys())e.startsWith(m)&&!_.has(e)&&this._forgetGlowSource(e);if(!u.length)return j``;const g=this._spaceWalls,f=o.length===n.length?[]:this._openCuts(),v=this._wallUnionGeometry()?.roomGeom,y=o.length===n.length?null:o.map(({r:t,poly:i})=>{const s=g.length&&t.id&&this._innerRoomContour(e,t.id,f,v)||i,o=this._cleanFloor(t,s,e).path,a=Pi(s,n.filter(e=>e.r!==t).map(e=>e.poly)),r=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z";return[o||r(s),...a.map(r)].join(" ")}),b=this._viewOr(this._baseVb()),w=this._stageEl?.clientWidth&&b.w?this._stageEl.clientWidth/b.w:1,k=1/(w>0?w:1),x=!this._pinchStart&&!this._panStart&&Date.now()>=this._glowFeatherSuspendUntil;(null==this._glowFeatherUnits||x)&&(this._glowFeatherUnits=k);const $=this._glowFeatherUnits??k,S=4*$,M=u.reduce((e,t)=>({x:Math.min(e.x,t.pos.x-t.r-S),y:Math.min(e.y,t.pos.y-t.r-S),maxX:Math.max(e.maxX,t.pos.x+t.r+S),maxY:Math.max(e.maxY,t.pos.y+t.r+S),w:0,h:0}),{x:1/0,y:1/0,maxX:-1/0,maxY:-1/0,w:0,h:0});return M.w=M.maxX-M.x,M.h=M.maxY-M.y,j` ${Me(u,e=>e.key,e=>{const t=e.domId,i=e.geometry;return j` ${""} - ${cv.map(([t,i])=>j` + ${hv.map(([t,i])=>j` `)} @@ -5729,7 +5729,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${this._t("gs.glow_radius")} {const t=Kf(e.target.value);null!=t&&t>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:t})}} /> + @input=${e=>{const t=Yf(e.target.value);null!=t&&t>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:t})}} /> ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")} @@ -5804,7 +5804,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 `:G} -
${this._t("gs.about_version",{v:qf})}
+
${this._t("gs.about_version",{v:Wf})}
${this._t("gs.about_github")} @@ -5861,7 +5861,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${e.busy?"…":this._t("btn.save")} -
`}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(ov,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>W` +
`}_saveKioskScale(e){this._kioskScale={...this._kioskScale,...e};try{localStorage.setItem(av,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const e=this._kioskScale,t=(t,i)=>W`
${this._rangeInput(50,300,5,Math.round(100*e[t]),e=>this._saveKioskScale({[t]:e/100}))} ${Math.round(100*e[t])}% @@ -5918,7 +5918,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 ${this._spaceDialog?this._renderSpaceDialog():G} ${this._importDialog?this._renderImportDialog():G} ${this._toast?W``:G} - `;const s=this._spaceModel();if(!s)return G;const n="valid"===i.kind?[s]:e,o=s.vb,a=this._effectiveProjection();this._renderProjection=a,this._labs.active.length&&function(e){Vg||Qg(e);const t=Gg,i=t.active.join(",");if(!i||i===Yg)return;Yg=i;const s=new Map(Ng.map(e=>[e.id,e])),n=t.active.map(e=>{const t=s.get(e);return`${e} (#${t.issue}, expires ${t.expires})`}).join(", ");console.info(`HOUSEPLAN LABS: ${n}`)}(qf);const r="iso"===a,l="devices"===this._mode&&this._showAll,c=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||l)),h=this._renderDeviceSnapshot,d=this._spaceDisplayForRender(),p=r?this._isoDecorationLayers(d):null,u=p?this._isoOpeningPanels(p):[],_=this._resolvedRoomFills(s,d),m=this._resolvedGlowBase(s,d,_),g=d.showLqi??this._config.show_signal??!0,f=this._config.icon_size??2.5,v=f>8?2.5:f,y=d_(v),b=this._viewOr(this._baseVb()),w=this._floorView(b),k=this._editing?"":this._stageBg(d),x=this._opMeasureView,$=this._decorMeasure,S=this._bdLive,M=this._furnLive,C="view"===this._mode?this._editorChromeMode:this._mode,D=this._roomHoverPaths(s),P=s.bg?this._display(s.bg.href):"",T=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,I=this._modeTransitionVisual,R=this._dayCycleState(),A=I?.viewWeight??("view"===this._mode?1:0),z=this._modeTransition.state?.from.presentedMode,F=!this._markup||!!I&&("view"===I.presentedMode||"devices"===I.presentedMode||"view"===z||"devices"===z),E=I?.stageColor||k,N=I?.sceneBrightness??1;return W` + `;const s=this._spaceModel();if(!s)return G;const n="valid"===i.kind?[s]:e,o=s.vb,a=this._effectiveProjection();this._renderProjection=a,this._labs.active.length&&function(e){Kg||ef(e);const t=Vg,i=t.active.join(",");if(!i||i===Jg)return;Jg=i;const s=new Map(Og.map(e=>[e.id,e])),n=t.active.map(e=>{const t=s.get(e);return`${e} (#${t.issue}, expires ${t.expires})`}).join(", ");console.info(`HOUSEPLAN LABS: ${n}`)}(Wf);const r="iso"===a,l="devices"===this._mode&&this._showAll,c=this._renderDevices.filter(e=>e.space===s.id&&(!e.hidden||l)),h=this._renderDeviceSnapshot,d=this._spaceDisplayForRender(),p=r?this._isoDecorationLayers(d):null,u=p?this._isoOpeningPanels(p):[],_=this._resolvedRoomFills(s,d),m=this._resolvedGlowBase(s,d,_),g=d.showLqi??this._config.show_signal??!0,f=this._config.icon_size??2.5,v=f>8?2.5:f,y=p_(v),b=this._viewOr(this._baseVb()),w=this._floorView(b),k=this._editing?"":this._stageBg(d),x=this._opMeasureView,$=this._decorMeasure,S=this._bdLive,M=this._furnLive,C="view"===this._mode?this._editorChromeMode:this._mode,D=this._roomHoverPaths(s),P=s.bg?this._display(s.bg.href):"",T=this._continuity.overlayVisible||"recovery-error"===this._continuity.state?this._continuity.recoveryReason:null,I=this._modeTransitionVisual,R=this._dayCycleState(),A=I?.viewWeight??("view"===this._mode?1:0),z=this._modeTransition.state?.from.presentedMode,F=!this._markup||!!I&&("view"===I.presentedMode||"devices"===I.presentedMode||"view"===z||"devices"===z),E=I?.stageColor||k,N=I?.sceneBrightness??1;return W` + transform=${p?.structural?af():G}> ${""} ${this._wallHatchDefs(d.color)}${j`${this._paperShapes(s.rooms).map(e=>"path"in e?j``:"poly"in e?j``:j``)}`} ${this._editing?this._renderMarkupDefs(o):G} @@ -6039,7 +6039,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 transform=${s.bg.angle?`rotate(${s.bg.angle} ${s.bg.x+s.bg.w/2} ${s.bg.y+s.bg.h/2})`:G} @dblclick=${e=>this._openBackdropDialog(e)} preserveAspectRatio="none" />`:G} - ${(()=>{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,o=e=>(n.has(e)||n.set(e,hi(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||d.showBorders).map(n=>{let a="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(a+=" picked");let r="";const l=zs(d.fill,n);if(!this._markup&&(d.showBorders||"none"!==l)){a+=" styled";const e=[];e.push(`--room-stroke:${d.color}`,`--room-stroke-op:${d.showBorders?d.opacity:0}`);const t=_.byRoom.get(n)||null;t?(a+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),r=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",g?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},p=o(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),m=p&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return p.some((e,i)=>Hs(t,[e[0],e[1],...p[(i+1)%p.length]])<=.08*this._gridPitch)}):[],f=u?[]:t,v=m.concat(f);v.length&&(a+=" noedge");const y=this._spaceWalls.length&&n.id&&p&&this._innerRoomContour(s,n.id,e,i)||p,b=y?Pi(y,(w=n,s.rooms.filter(e=>e!==w).map(o).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=y?this._cleanFloor(n,y,s).path:"",$=n.id||G,S=n.area||G,M=x&&y?j`{const e=this._openCuts(),t=this._thickWallCuts(),i=this._wallUnionGeometry()?.roomGeom,n=new Map,o=e=>(n.has(e)||n.set(e,hi(e)),n.get(e));return s.rooms.filter(e=>e.area||"view"===this._mode||this._markup||d.showBorders).map(n=>{let a="room "+(s.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||n.id!==this._mergeSel&&n.id!==this._splitSel?.roomId||(a+=" picked");let r="";const l=zs(d.fill,n);if(!this._markup&&(d.showBorders||"none"!==l)){a+=" styled";const e=[];e.push(`--room-stroke:${d.color}`,`--room-stroke-op:${d.showBorders?d.opacity:0}`);const t=_.byRoom.get(n)||null;t?(a+=" filled",e.push(`--room-fill:${t.color}`,`--room-fill-op:${t.opacity}`)):e.push("--room-fill:transparent","--room-fill-op:0"),r=e.join(";")}let c;const h=e=>{"view"===this._mode&&(void 0===c&&(c=this._roomArea(n)),this._showTip(e,n.name||this._t("room.unnamed"),c?this._t("tip.area",{value:c}):"",g?this._roomLqi(n.area):null,this._roomTemp(n),this._roomHum(n)))},p=o(n),u=this._markup&&(n.id===this._mergeSel||n.id===this._splitSel?.roomId),m=p&&!u?e.filter(e=>{const t=[(e[0]+e[2])/2,(e[1]+e[3])/2];return p.some((e,i)=>Ls(t,[e[0],e[1],...p[(i+1)%p.length]])<=.08*this._gridPitch)}):[],f=u?[]:t,v=m.concat(f);v.length&&(a+=" noedge");const y=this._spaceWalls.length&&n.id&&p&&this._innerRoomContour(s,n.id,e,i)||p,b=y?Pi(y,(w=n,s.rooms.filter(e=>e!==w).map(o).filter(Boolean))):[];var w;const k=e=>"M "+e.map(e=>e[0]+" "+e[1]).join(" L ")+" Z",x=y?this._cleanFloor(n,y,s).path:"",$=n.id||G,S=n.area||G,M=x&&y?j`{this._notePointer(e),this._pointerModality.hoverEnabled&&(this._hoverRoom={space:s.id,room:n})}} @@ -6226,7 +6226,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 `:G} ${this._toast?W``:G} - `}_vacCandidateStatus(e,t,i=this._planHass){const s=this._bindingStatus("entity:"+e);if("ha_disabled"===s.kind)return"disabled";if("orphaned"===s.kind)return"missing";if("unverified"===s.kind)return"unverified";const n=i?.states?.[e];return"unavailable"===n?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,s]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const n=jd(e,s,t[e]);n&&i.push(n)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const s=e.marker?.vacuum,n="string"==typeof s?.source&&!!s.source,o=new Set(e.entities||[]),a=new Set(o);n&&a.add(s.source);const r=i?.entities||{},l=[];for(const e of a){const t=i?.states?.[e],o=jd(e,t,r[e]);o?l.push(o):n&&e===s.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:r[e]?.platform?String(r[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,s){const n="string"==typeof e&&e.length>0,o=new Set(t),a=Array.from(i).sort((t,i)=>n&&t.entityId===e?-1:n&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(n){const t=a.find(t=>t.entityId===e);return{entityId:e,status:s[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:a}}const r=a.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===s[e.entityId]);return r?{entityId:r.entityId,status:"ok",pinned:!1,candidates:a}:{entityId:null,status:"none",pinned:!1,candidates:a}}(s?.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=ho(this._planHass,this._devices,null,this._virtualLights)){const i=t_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:n_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=O_(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),L_(s,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=ho(this._planHass,this._devices,null,this._virtualLights);for(const s of this._devices){if(s.hidden)continue;if("icon_ripple"!==Bi(s.marker?.display))continue;t.add(s.id);const n=this._activitySnapshot(s,i);e.set(s.id,n);const{samples:o,sourceKey:a}=n;let r=this._activityRt.get(s.id);r?r.sources!==a&&H_(r,a,o,window.clearTimeout.bind(window)):(r=O_(a,o),this._activityRt.set(s.id,r))}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"!==Bi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:s,sourceKey:n}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==n)continue;const a=B_(o,s,window.clearTimeout.bind(window));a&&this._stampActivity(t.id,a,n)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===Bi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),s=Kd(this.hass.states[i||""]?.state),n=Wd(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)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const a="never"!==Qd(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const r=n?.pos;if(s&&r){const e=r.x+":"+r.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=[r.x,r.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 s={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(s),s}_renderVacSection(e){const t=this._devices.find(t=>t.id===e.devId);if(!t||!this._isVacDev(t))return G;const i=t.marker?.vacuum||{},s=this._vacAllCamerasFor===t.id,n=this._vacSourceResolution(t,s),o=n.entityId,a=n.candidates.find(e=>e.entityId===o)||null,r=o?Wd(this.hass?.states[o]?.attributes):null,l=!1!==i.live&&("ok"===n.status||"unsupported"===n.status),c=this._vacPlanRoomAnchors(t.space),h=r?function(e,t){const i=new Set(t.map(Ud).filter(Boolean)),s=new Set;for(const t of e){const e=Ud(t.name||"");e&&i.has(e)&&s.add(e)}return s.size}(r.rooms,c.map(e=>e.name)):0,d=!!(l&&r&&h>=3),p=Object.keys(i.calibration||{}),u=e=>{const i=this._vacEnsureMarker(t);i&&(i.vacuum={...i.vacuum||{},...e},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate())},_=new Set(t.entities||[]),m=n.candidates.filter(e=>_.has(e.entityId)||e.entityId===i.source),g=s?n.candidates.filter(e=>e.entityId.startsWith("camera.")&&!m.some(t=>t.entityId===e.entityId)):[],f=e=>[e.hasPosition?this._t("vac.cap_position"):"",e.hasRooms?this._t("vac.cap_rooms_short"):"",e.hasPath?this._t("vac.cap_path"):"",e.hasMapId?this._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this._t("vac.cap_none"),v=e=>W` + `}_vacCandidateStatus(e,t,i=this._planHass){const s=this._bindingStatus("entity:"+e);if("ha_disabled"===s.kind)return"disabled";if("orphaned"===s.kind)return"missing";if("unverified"===s.kind)return"unverified";const n=i?.states?.[e];return"unavailable"===n?.state?"unavailable":t?.hasPosition?"ok":"unsupported"}_vacOpenAllCameras(e){const t=this._haRegistry.entities||{},i=[];for(const[e,s]of Object.entries(this.hass?.states||{})){if(!e.startsWith("camera."))continue;const n=jd(e,s,t[e]);n&&i.push(n)}this._vacAllCameraCache={devId:e.id,candidates:i},this._vacAllCamerasFor=e.id}_vacSourceResolution(e,t=!1,i=this._planHass){const s=e.marker?.vacuum,n="string"==typeof s?.source&&!!s.source,o=new Set(e.entities||[]),a=new Set(o);n&&a.add(s.source);const r=i?.entities||{},l=[];for(const e of a){const t=i?.states?.[e],o=jd(e,t,r[e]);o?l.push(o):n&&e===s.source&&l.push({entityId:e,name:String(t?.attributes?.friendly_name||e),platform:r[e]?.platform?String(r[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,s){const n="string"==typeof e&&e.length>0,o=new Set(t),a=Array.from(i).sort((t,i)=>n&&t.entityId===e?-1:n&&i.entityId===e?1:i.score-t.score||t.entityId.localeCompare(i.entityId));if(n){const t=a.find(t=>t.entityId===e);return{entityId:e,status:s[e]||(t?.hasPosition?"ok":"unverified"),pinned:!0,candidates:a}}const r=a.find(e=>o.has(e.entityId)&&e.hasPosition&&"ok"===s[e.entityId]);return r?{entityId:r.entityId,status:"ok",pinned:!1,candidates:a}:{entityId:null,status:"none",pinned:!1,candidates:a}}(s?.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=ho(this._planHass,this._devices,null,this._virtualLights)){const i=i_(this._planHass,e,this._devices,t,this._fullRegistryHass);return{samples:i.samples,sourceKey:o_(this._planHass,e,i)}}_stampActivity(e,t,i){let s=this._activityRt.get(e);s||(s=L_(i||"",[]),this._activityRt.set(e,s)),null!=i&&(s.sources=i),B_(s,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=ho(this._planHass,this._devices,null,this._virtualLights);for(const s of this._devices){if(s.hidden)continue;if("icon_ripple"!==Bi(s.marker?.display))continue;t.add(s.id);const n=this._activitySnapshot(s,i);e.set(s.id,n);const{samples:o,sourceKey:a}=n;let r=this._activityRt.get(s.id);r?r.sources!==a&&H_(r,a,o,window.clearTimeout.bind(window)):(r=L_(a,o),this._activityRt.set(s.id,r))}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"!==Bi(t.marker?.display))continue;const i=e.get(t.id)||this._activitySnapshot(t),{samples:s,sourceKey:n}=i,o=this._activityRt.get(t.id);if(!o||o.sources!==n)continue;const a=q_(o,s,window.clearTimeout.bind(window));a&&this._stampActivity(t.id,a,n)}}_vacTick(){if(this.hass)for(const e of this._devices){if(e.hidden||!this._isVacDev(e))continue;if("static_icon"===Bi(e.marker?.display)){this._vacRt.delete(e.id);continue}const t=this._vacSource(e);if(!t)continue;const i=this._vacEntity(e),s=Kd(this.hass.states[i||""]?.state),n=Wd(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)),s&&!o.moving&&(o.trail=[],o.lastPos=null);const a="never"!==Qd(e.marker?.vacuum)&&!n?.path.length;!s&&o.moving&&(o.endedTs=Date.now(),a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=null),o.moving=s;const r=n?.pos;if(s&&r){const e=r.x+":"+r.y;if(e!==o.lastKey){const t=Date.now();o.jump=o.lastTs>0&&t-o.lastTs>1e4,o.lastKey=e,o.lastTs=t,a&&o.lastPos&&(o.trail=Vd(o.trail,o.lastPos,40)),o.lastPos=[r.x,r.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 s={id:e.id,binding:e.bindingKind+":"+e.bindingRef,space:e.space||null,area:e.area||null,hidden:!!e.hidden};return t.markers.push(s),s}_renderVacSection(e){const t=this._devices.find(t=>t.id===e.devId);if(!t||!this._isVacDev(t))return G;const i=t.marker?.vacuum||{},s=this._vacAllCamerasFor===t.id,n=this._vacSourceResolution(t,s),o=n.entityId,a=n.candidates.find(e=>e.entityId===o)||null,r=o?Wd(this.hass?.states[o]?.attributes):null,l=!1!==i.live&&("ok"===n.status||"unsupported"===n.status),c=this._vacPlanRoomAnchors(t.space),h=r?function(e,t){const i=new Set(t.map(Ud).filter(Boolean)),s=new Set;for(const t of e){const e=Ud(t.name||"");e&&i.has(e)&&s.add(e)}return s.size}(r.rooms,c.map(e=>e.name)):0,d=!!(l&&r&&h>=3),p=Object.keys(i.calibration||{}),u=e=>{const i=this._vacEnsureMarker(t);i&&(i.vacuum={...i.vacuum||{},...e},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate())},_=new Set(t.entities||[]),m=n.candidates.filter(e=>_.has(e.entityId)||e.entityId===i.source),g=s?n.candidates.filter(e=>e.entityId.startsWith("camera.")&&!m.some(t=>t.entityId===e.entityId)):[],f=e=>[e.hasPosition?this._t("vac.cap_position"):"",e.hasRooms?this._t("vac.cap_rooms_short"):"",e.hasPath?this._t("vac.cap_path"):"",e.hasMapId?this._t("vac.cap_map"):""].filter(Boolean).join(" · ")||this._t("vac.cap_none"),v=e=>W`
`)}return this._vacLastView=t,n.length&&!this._vacRaf&&this._vacRafLoop(),n.length||o.length?W` ${o.length?j`${o}`:G} - ${n}`:G}_renderDevice(e,t,i=!0){const s=this._pos(e),n=this._scenePoint([s.x,s.y]),o=(n[0]-t.x)/t.w*100,a=(n[1]-t.y)/t.h*100,r=this._devicePresentation(e,i),l=[`left:${o}%`,`top:${a}%`,...Go(r)],c=r.disabledReason,h=r.haDisabled?this._t(`marker.ha_disabled_${c}`):e.userHidden?this._t("marker.hidden_ghost"):e.name,d=Yu(r),p="view"===this._mode||"devices"===this._mode,u=[h,r.haDisabled?"":this._t(`marker.state_a11y_${d}`),"none"!==r.pulse.kind?this._t(`marker.pulse_a11y_${r.pulse.reason}`):"",r.valueFullText||r.valueText||"",Io(r.valueBadge),null!=r.lqiText&&r.lqiBand?this._t(`marker.lqi_a11y_${r.lqiBand}`,{value:r.lqiText}):""].filter(Boolean).join(", "),_=[e.model,r.valueBadge?.fullText||"",null!=r.lqiText?"LQI "+r.lqiText:""].filter(Boolean).join(" · ");return W`
`}_roomArea(e){const t=hi(e);if(!t)return null;const i=this._spaceModel();if(!i)return null;const s=this._spaceWalls.length&&e.id&&this._innerRoomContour(i,e.id)||t,n=this._cleanFloor(e,s),o=this._cellCm/this._gridPitch;return ir(n.area*o*o/1e4,"mi"===this._renderPlanHass?.config?.unit_system?.length)}_roomTemp(e){const t=e.settings?.temp_source;return t?Mo(this._renderPlanHass,t,"temp",this._markers):e.area?this._climate().get(e.area)?.temp??null:null}_roomHum(e){const t=e.settings?.hum_source;return t?Mo(this._renderPlanHass,t,"hum",this._markers):e.area?this._climate().get(e.area)?.hum??null:null}_climate(){const e=this._serverCfg?.markers,t=this._renderPlanHass,i=this._climateCache;if(i&&i.h===t&&i.r===this._iconRules&&i.mk===e)return i.m;const s=function(e,t,i){const s=new Map;if(!e?.entities)return s;const n=wo(i),o=new Set;for(const e of i||[]){if(e?.removed||!0!==e?.use_climate_temp)continue;const t=(e.binding||"").indexOf(":");t>0&&o.add(e.binding.slice(t+1))}const a=new Map;for(const[t,i]of Object.entries(e.entities)){if(i.device_id&&n.devices.has(i.device_id)&&!n.liveEntities.has(t)||!i.device_id&&n.entities.has(t)&&!n.liveEntities.has(t))continue;const s=i.device_id?e.devices?.[i.device_id]:null,r=i.area_id||s?.area_id||null;if(!r)continue;if(i.entity_category)continue;if(!(o.size>0&&t.startsWith("climate.")&&(o.has(t)||i.device_id&&o.has(i.device_id)))){if(Ye.has(i.platform))continue;if(Co.test(t))continue}let l=a.get(r);l||(l=new Map,a.set(r,l));const c=i.device_id||t;let h=l.get(c);if(!h){const n=e.states?.[t];h={name:(s?s.name_by_user||s.name:i.name||n?.attributes?.friendly_name||t)||t,model:s?.model,ents:[]},l.set(c,h)}h.ents.push(t)}for(const[i,n]of a){const a=[],r=[];for(const[i,s]of n){const n=bo(e,s.name,s.model,s.ents,t),l="mdi:thermometer"===n||"mdi:air-filter"===n;if(l){const t=mo(e,s.ents);null!=t&&a.push(t)}if(o.size>0&&(o.has(i)||s.ents.some(e=>o.has(e)))){const t=go(e,s.ents);null!=t&&a.push(t)}if(l||"mdi:water-percent"===n){const t=vo(e,s.ents);null!=t&&r.push(t)}}(a.length||r.length)&&s.set(i,{temp:a.length?Math.round(a.reduce((e,t)=>e+t,0)/a.length*10)/10:null,hum:r.length?Math.round(r.reduce((e,t)=>e+t,0)/r.length):null})}return s}(t,this._iconRules,e);return this._climateCache={h:t,r:this._iconRules,mk:e,m:s},s}_resetRoomDialogFields(){this._roomEditId=null,this._roomFill="",this._roomCustomFill=null,this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1}_openRoomEdit(e){if(!e.id)return;this._roomEditId=e.id,this._nameSel=e.name||"",this._areaSel=e.area||"",this._roomFill="glow"===e.settings?.fill_mode?"":e.settings?.fill_mode||"";const t=e.settings?.custom_fill;this._roomCustomFill=t&&"object"==typeof t?rs(t,cs(this._curSpaceCfg).customFill):null,this._roomTempSrc=e.settings?.temp_source||"",this._roomHumSrc=e.settings?.hum_source||"",this._roomNameScale=Es(e.settings?.name_scale),this._roomLabelScale=Es(e.settings?.label_scale),this._roomSrcOpen=null,this._roomSrcFilter="",this._roomDialog=!0}_roomSettingsFromDialog(){const e={};return this._roomFill&&(e.fill_mode=this._roomFill),this._roomCustomFill&&(e.custom_fill=this._roomCustomFill),this._roomTempSrc&&(e.temp_source=this._roomTempSrc),this._roomHumSrc&&(e.hum_source=this._roomHumSrc),1!==this._roomNameScale&&(e.name_scale=this._roomNameScale),1!==this._roomLabelScale&&(e.label_scale=this._roomLabelScale),Object.keys(e).length?e:null}_saveRoomEdit(){const e=this._curSpaceCfg,t=e?.rooms.find(e=>e.id===this._roomEditId);if(!t)return this._roomDialog=!1,void(this._roomEditId=null);t.name=this._nameSel.trim()||t.name,t.area=this._areaSel||null;const i=t.settings||{},s={...i};this._roomFill?s.fill_mode=this._roomFill:delete s.fill_mode,this._roomCustomFill?s.custom_fill=this._roomCustomFill:delete s.custom_fill,"glow"===i.fill_mode&&"boolean"!=typeof i.glow&&(s.glow=!0),this._roomTempSrc?s.temp_source=this._roomTempSrc:delete s.temp_source,this._roomHumSrc?s.hum_source=this._roomHumSrc:delete s.hum_source,1!==this._roomNameScale?s.name_scale=this._roomNameScale:delete s.name_scale,1!==this._roomLabelScale?s.label_scale=this._roomLabelScale:delete s.label_scale,Object.keys(s).length?t.settings=s:delete t.settings,this._saveConfig(),this._roomDialog=!1,this._roomEditId=null,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate(),this._showToast(this._t("toast.room_updated"))}_roomSrcCandidates(){const e=this._planHass,t=wo(this._markers),i=this._roomSrcFilter.trim().toLowerCase(),s=[];for(const n of Object.values(e.devices)){if("service"===n.entry_type)continue;if(t.devices.has(n.id))continue;const e=(n.name_by_user||n.name||n.id).trim();i&&!e.toLowerCase().includes(i)||s.push({value:"device:"+n.id,label:e,sub:n.model||this._t("marker.sub_device")})}for(const[n,o]of Object.entries(e.entities)){if(!n.startsWith("sensor.")||o.hidden)continue;if(ko(e,n,t))continue;const a=o.name||e.states[n]?.attributes?.friendly_name||n;i&&!(a+" "+n).toLowerCase().includes(i)||s.push({value:"entity:"+n,label:a,sub:n})}return s.sort((e,t)=>e.label.localeCompare(t.label)),s.slice(0,200)}_roomSrcLabel(e){const t=e.indexOf(":"),i=e.slice(0,t),s=e.slice(t+1);return"device"===i?this._fullRegistryHass.devices[s]?.name_by_user||this._fullRegistryHass.devices[s]?.name||s:this._fullRegistryHass.entities[s]?.name||this.hass.states[s]?.attributes?.friendly_name||s}_labelPos(e,t){const i=this._layout["rl_"+(e.id||"")];if(i&&i.s===t)return{x:i.x*lv,y:i.y*lv};const s=this._snap(this._roomCenter(e));return{x:s[0],y:s[1]}}_labelDown(e,t,i){if("plan"!==this._mode)return;e.preventDefault(),e.stopPropagation();const s=this._labelPos(t,i);this._drag={id:"rl_"+(t.id||""),sx:e.clientX,sy:e.clientY,ox:s.x,oy:s.y,moved:!1},gv(e),this._tip=null}_labelMove(e,t,i){const s="rl_"+(t.id||"");if(!this._drag||this._drag.id!==s)return;const n=this._stageEl;if(!n)return;const o=this._spaceModelById(i);if(!o)return;const a=o.vb,r=n.getBoundingClientRect(),l=this._viewOr(a),c=(e.clientX-this._drag.sx)/r.width*l.w,h=(e.clientY-this._drag.sy)/r.height*l.h;Math.abs(e.clientX-this._drag.sx)+Math.abs(e.clientY-this._drag.sy)>3&&(this._drag.moved=!0);const d=Oh(this._drag.ox+c),p=Oh(this._drag.oy+h);this._savePos({id:s,space:i},d,p)}_labelUp(e){const t="rl_"+(e.id||"");if(!this._drag||this._drag.id!==t)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&window.setTimeout(()=>this._drag=null,0)}_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){if("plan"!==this._mode)return;e.preventDefault(),e.stopPropagation();const s=e.target.closest(".roomlabel");if(!s)return;const n=s.getBoundingClientRect(),o=n.left+n.width/2,a=n.top+n.height/2,r=Math.max(8,Math.hypot(e.clientX-o,e.clientY-a));this._rlResize={id:"rl_"+(t.id||""),space:i,k0:this._labelScale(t),cx:o,cy:a,d0:r},gv(e)}_rlResizeMove(e){const t=this._rlResize;if(!t)return;e.stopPropagation();const i=Math.max(8,Math.hypot(e.clientX-t.cx,e.clientY-t.cy)),s=Math.min(3,Math.max(.5,t.k0*(i/t.d0))),n=this._layout[t.id];if(n)this._layout={...this._layout,[t.id]:{...n,k:s}};else{const e=t.id.slice(3),i=this._spaceModelById(t.space);if(!i)return;const n=i.rooms.find(t=>t.id===e);if(!n)return;const o=this._labelPos(n,t.space);this._layout={...this._layout,[t.id]:{s:t.space,x:o.x/lv,y:o.y/lv,k:s}}}this._dirtyPos.add(t.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(e,t,i){if(!e.id)return G;let s=null;if(e.poly?(s=this._gearPtCache.get(e.poly)||null,s||(s=$i(e.poly),this._gearPtCache.set(e.poly,s))):null!=e.x&&null!=e.y&&(s=[e.x+(e.w||0)/2,e.y+(e.h||0)/2]),!s)return G;const n=(s[0]-i.x)/i.w*100,o=(s[1]-i.y)/i.h*100;return W`
`}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,s=(i[0]-e.x)/e.w*100,n=(i[1]-e.y)/e.h*100,o=Ns(t,i),a=Math.round(10*o)/10,r=function(e,t,i=.001){if(e.length<2||t.length<2)return!1;const s=Math.abs(t[0]-e[0]),n=Math.abs(t[1]-e[1]);if(!Number.isFinite(s)||!Number.isFinite(n)||Math.hypot(s,n)<=i)return!1;const o=Math.max(s,n,1);return s<=i*o||n<=i*o||Math.abs(s-n)<=i*o}(t,i,2e-4*this._gridPitch);return W`
- ${this._fmtLen(t,i)} · ${a}°
`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[s,n]=e.b;if(Math.abs(t-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(t+s)/2,a=(i+n)/2;if("line"===e.kind){const t=Ns(e.a,e.b);return{x:o,y:a,on45:Os(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const r=this._fmtLen([t,i],[s,i]),l=this._fmtLen([s,i],[s,n]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(s-t)/2,0]),r=this._fmtLen([0,0],[0,Math.abs(n-i)/2]);return{x:o,y:a,on45:!1,text:Math.abs(Math.abs(s-t)-Math.abs(n-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${r}`}}return{x:o,y:a,on45:!1,text:`${r} × ${l} · ${ir(Br(Math.abs(s-t),this._cellCm,this._gridPitch)*Br(Math.abs(n-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._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.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=lv,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){const e=[],t=this._spaceModel();if(this._markup){if(!t)return e;if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of t.rooms){if(!s.name||s.id===i)continue;const t=this._labelPos(s,this._space);e.push([t.x,t.y])}return e}for(const i of t.rooms){const t=hi(i);if(t)for(const i of t)e.push(i)}if("draw"===this._tool)for(const t of this._path)e.push(t);if("split"===this._tool&&this._splitSel?.pts)for(const t of this._splitSel.pts)e.push(t);return e}if("devices"===this._mode){for(const t of this._devices){if(t.space!==this._space||t.id===this._drag?.id||"ha_disabled"===t.bindingStatus?.kind)continue;const i=this._pos(t);e.push([i.x,i.y])}return e}if("decor"===this._mode){const t=this._decorMove?.id;return e.push(...this._decorSnapGeometry(t).points),this._decorDraft&&e.push(this._decorDraft.a),e}return e}_renderAlignGuides(){const e=this._alignPoint;if(!e)return j``;const t=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(e,t,i){let s=null,n=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&&(!s||t1e-6&&(!n||t + ${this._fmtLen(t,i)} · ${a}°`}get _decorMeasure(){const e=this._decorDraft;if(!e||"decor"!==this._mode)return null;const[t,i]=e.a,[s,n]=e.b;if(Math.abs(t-s)<1e-6&&Math.abs(i-n)<1e-6)return null;const o=(t+s)/2,a=(i+n)/2;if("line"===e.kind){const t=Ns(e.a,e.b);return{x:o,y:a,on45:Os(t),text:`${this._fmtLen(e.a,e.b)} · ${Math.round(10*t)/10}°`}}const r=this._fmtLen([t,i],[s,i]),l=this._fmtLen([s,i],[s,n]);if("ellipse"===e.kind){const e=this._fmtLen([0,0],[Math.abs(s-t)/2,0]),r=this._fmtLen([0,0],[0,Math.abs(n-i)/2]);return{x:o,y:a,on45:!1,text:Math.abs(Math.abs(s-t)-Math.abs(n-i))<1e-6?`R ${e}`:`Rx ${e} × Ry ${r}`}}return{x:o,y:a,on45:!1,text:`${r} × ${l} · ${ir(Br(Math.abs(s-t),this._cellCm,this._gridPitch)*Br(Math.abs(n-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._drag?.moved){const e=this._devices.find(e=>e.id===this._drag.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=cv,i=this._decorH;return"line"===e.kind?[e.x1*t,e.y1*i]:[e.x*t,e.y*i]}return null}return null}_alignCandidates(){const e=[],t=this._spaceModel();if(this._markup){if(!t)return e;if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of t.rooms){if(!s.name||s.id===i)continue;const t=this._labelPos(s,this._space);e.push([t.x,t.y])}return e}for(const i of t.rooms){const t=hi(i);if(t)for(const i of t)e.push(i)}if("draw"===this._tool)for(const t of this._path)e.push(t);if("split"===this._tool&&this._splitSel?.pts)for(const t of this._splitSel.pts)e.push(t);return e}if("devices"===this._mode){for(const t of this._devices){if(t.space!==this._space||t.id===this._drag?.id||"ha_disabled"===t.bindingStatus?.kind)continue;const i=this._pos(t);e.push([i.x,i.y])}return e}if("decor"===this._mode){const t=this._decorMove?.id;return e.push(...this._decorSnapGeometry(t).points),this._decorDraft&&e.push(this._decorDraft.a),e}return e}_renderAlignGuides(){const e=this._alignPoint;if(!e)return j``;const t=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(e,t,i){let s=null,n=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&&(!s||t1e-6&&(!n||t ${i.map(t=>{const[i,o,a,r]="x"===t.axis?[t.at,t.from[1],t.at,e[1]+Math.sign(e[1]-t.from[1])*n]:[t.from[0],t.at,e[0]+Math.sign(e[0]-t.from[0])*n,t.at];return j` `})} @@ -6438,7 +6438,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 -
`}_renderOpeningDialog(){const e=this._openingDialog,t=this._spaceModel()?.partitions||[],i=e.id?this._curSpaceCfg?.openings?.find(t=>t.id===e.id):null,s={id:e.id||"preview",type:e.type,x:e.x/lv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/lv,...e.host?{host:e.host}:{}},n=Rp(i,s),o=e.host?n?Ip(s,t,lv,this._cellCm,this._gridPitch):Tp(s,t,lv,this._cellCm,this._gridPitch):null,a="does-not-fit-jamb"===o?.reason,r=e.host?t.find(t=>t.id===e.host.id):null,l=r?li(r.cm/2,this._imperial):"",c=!!e.host&&!o?.resolved&&!a,h="gate"===e.type?"mdi:gate":"window"===e.type?"mdi:window-closed-variant":"passage"===e.type?"mdi:arch":"mdi:door",d=(t,i)=>{const s="contact"===t?e.contact:e.lock,n="contact"===t?!!e.contactOpen:!!e.lockOpen,o="contact"===t?e.contactFilter||"":e.lockFilter||"",a=i.find(e=>e.value===s),r=a?.label||this.hass.states[s]?.attributes?.friendly_name||this._fullRegistryHass.entities[s]?.name||s,l=function(e,t,i=200){const s=t.trim().toLowerCase();return(s?e.filter(e=>e.label.toLowerCase().includes(s)||e.value.toLowerCase().includes(s)):e).slice(0,Math.max(0,i))}(i,o);return W` +
`}_renderOpeningDialog(){const e=this._openingDialog,t=this._spaceModel()?.partitions||[],i=e.id?this._curSpaceCfg?.openings?.find(t=>t.id===e.id):null,s={id:e.id||"preview",type:e.type,x:e.x/cv,y:e.y/this._spaceH,angle:e.angle,length:i&&!e.lengthTouched?i.length:this._cmToUnits(Math.max(20,e.lengthCm))/cv,...e.host?{host:e.host}:{}},n=Rp(i,s),o=e.host?n?Ip(s,t,cv,this._cellCm,this._gridPitch):Tp(s,t,cv,this._cellCm,this._gridPitch):null,a="does-not-fit-jamb"===o?.reason,r=e.host?t.find(t=>t.id===e.host.id):null,l=r?li(r.cm/2,this._imperial):"",c=!!e.host&&!o?.resolved&&!a,h="gate"===e.type?"mdi:gate":"window"===e.type?"mdi:window-closed-variant":"passage"===e.type?"mdi:arch":"mdi:door",d=(t,i)=>{const s="contact"===t?e.contact:e.lock,n="contact"===t?!!e.contactOpen:!!e.lockOpen,o="contact"===t?e.contactFilter||"":e.lockFilter||"",a=i.find(e=>e.value===s),r=a?.label||this.hass.states[s]?.attributes?.friendly_name||this._fullRegistryHass.entities[s]?.name||s,l=function(e,t,i=200){const s=t.trim().toLowerCase();return(s?e.filter(e=>e.label.toLowerCase().includes(s)||e.value.toLowerCase().includes(s)):e).slice(0,Math.max(0,i))}(i,o);return W` `:G} - `}_markerValueBadgeFields(e){return(t={touched:e.valueBadgeTouched,originalHas:e.originalHasValueBadge,original:e.originalValueBadge,enabled:e.valueBadgeEnabled,source:e.valueBadgeSource,position:e.valueBadgePosition}).touched?{value_badge:{enabled:t.enabled,source:t.source,position:t.position}}:t.originalHas?{value_badge:t.original}:{};var t}_markerDraft(e){if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return null;const t=qs(e.room),i=Fi(e.binding,e.devId,()=>"__hp_device_preview__"),s=this._markers.find(t=>t.id===i||t.id===e.devId),n=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),o=this._effectiveStoredTapAction(e),a={id:i,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle||null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===o&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:n.length?n:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Kf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:e.hideFromPlan,vacuum:s?.vacuum||null};return("virtual"===e.binding||e.room)&&(a.space=t?.space||("virtual"===e.binding?this._space:null),a.area=t?.area||null,a.room_id=t?.roomId||null),a}_markerPreviewDevice(e){const t=this._markerDraft(e);if(!t)return null;const i=`${this._haRegistry.revision}\n${Md(this._markers)}\n${JSON.stringify(t)}`;if(this._markerPreviewMemo?.key===i)return this._markerPreviewMemo.device;const s=function(e){const{marker:t,siblingMarkers:i=[],...s}=e;return So({...s,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),marker:t,siblingMarkers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||this._space,loc:e=>this._t(e),iconRules:this._iconRules});return this._markerPreviewMemo={key:i,device:s},s}_markerPreviewDevices(e){const t=this._markerPreviewDevicesMemo;if(t?.base===this._devices&&t.preview===e)return t.devices;const i=[...this._devices.filter(t=>t.id!==e.id),e];return this._markerPreviewDevicesMemo={base:this._devices,preview:e,devices:i},i}_toggleIntent(e,t=this._devices){return kp({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i=this._markerPreviewDevices(t);return this._toggleIntent(t,i)}_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 s={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 s?this._t(s):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(!$p(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,s=i?e.targets.filter(e=>"on"===e.state).length:0,n=i?0===s?t.groupAllOff():t.groupCurrent(s,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(n),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){if(!e)return[];const t=e=>this._t(`marker.toggle_effect_${e.replace("-","_")}`),i=e=>this._t(`marker.toggle_skip_${e.replace("-","_")}`);return function(e,t){const i=e.targets[0]||e.skippedTargets[0],s=[];return"group"===e.kind?e.targets.length&&s.push(t.group(e.targets)):i&&s.push(t.single(i)),e.nextEffect&&e.targets.length&&s.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&s.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&s.push(t.none(e.noneReason)),s}(e,{single:e=>{if("via"in e&&"virtual-light"===e.via)return this._t("marker.virtual_light_target",{name:e.name});const t=e.entityId||("ref"in e?e.ref:""),i=e.name||t;return this._t("marker.toggle_hint_single",{name:i,id:t})},group:e=>this._t("marker.toggle_hint_group",{count:e.length,names:e.map(e=>`${e.name} (${e.entityId})`).join(", ")}),currentNext:(e,i)=>"virtual-light"===e.via?this._t("marker.virtual_light_current",{state:this._t("on"===e.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:t(i)}):this._t("marker.toggle_hint_current",{state:this._toggleStateText(e.entityId,e.state),effect:t(i)}),groupCurrentNext:(e,i)=>this._t("marker.toggle_hint_group_current",{on:e.filter(e=>"on"===e.state).length,count:e.length,effect:t(i)}),skipped:e=>this._t("marker.toggle_hint_skipped",{count:e.length,targets:e.map(e=>{const t=e.entityId||e.ref;return`${e.name||t} (${t}: ${i(e.reason)})`}).join(", ")}),none:e=>this._t(`marker.toggle_none_${e.replaceAll("-","_")}`)})}_effectiveStoredTapAction(e,t){return e.tapActionTouched?e.tapAction:vp(e.originalHasTapAction?e.originalTapAction:null,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._effectiveStoredTapAction(e,t?.primary?.split(".")[0])}_announceToggleDraft(e){const t=this._markerPreviewDevice(e),i=e.tapActionTouched?e:{...e,tapAction:this._effectiveMarkerTapAction(e,t)},s="toggle"===i.tapAction?this._toggleHintLines(this._toggleIntentForDialog(i)).join(" "):"";return{...i,tapHintAnnouncement:s}}_valueBadgeForBinding(e,t){const i={...e,binding:t,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},s=this._markerPreviewDevice(i);if(!s)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const n=[...this._devices.filter(e=>e.id!==s.id),s],o=Wo(this._planHass,s,n),a=jo(this._planHass,s,o);return{valueBadgeEnabled:e.valueBadgeEnabled&&!!a,valueBadgeSource:a,valueBadgeTouched:!0}}_markerSpatialSource(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i={...t,hidden:!1};return po(ho(this._planHass,[...this._devices.filter(e=>e.id!==i.id),i],null,this._virtualLights).filter(e=>e.device.id===i.id))}_markerAutoHasSpatialSource(e){const t={...e,lightRole:"auto",lightRoleTouched:!0},i=this._markerPreviewDevice(t);return!!i&&lo(this._planHass,{...i})}_setMarkerLightRole(e){const t=this._markerDialog;t&&(this._markerDialog={...t,lightRole:e,lightRoleTouched:!0})}_controlRefInfo(e){if(!e.startsWith("marker:"))return{label:this.hass.states[e]?.attributes?.friendly_name||e,sub:e,icon:e.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this._planEntityAvailable(e)};const t=e.slice(7),i=this._markers.find(e=>e.id===t),s=this._devices.find(e=>e.id===t);if(!i||i.removed||!0!==i.is_light)return{label:this._t("marker.control_missing_label"),sub:`${this._t("marker.control_broken")} (${e})`,icon:"mdi:alert-outline",warning:!0};const n=this._serverCfg?.spaces.find(e=>e.id===(s?.space||i.space)),o=!!s&&eo(s).length>0;return{label:i.name||s?.name||t,sub:[n?.title||s?.space||i.space,i.room_id||s?.area,o?"":this._t("marker.control_passive")].filter(Boolean).join(" · "),icon:i.icon||s?.icon||"mdi:lightbulb-outline",warning:!0===i.hidden||!0===s?.hidden}}_valueBadgeCandidateLabel(e){const t=e.source;if("derived_lqi"===t.kind)return this._t("marker.value_badge_lqi");if("derived_marker_state"===t.kind)return this._t("marker.value_badge_marker_state",{name:e.label});if("entity_state"===t.kind)return this._t("marker.value_badge_state",{name:e.label});const i=this.hass.states[t.entity_id]?.attributes?.friendly_name||this._fullRegistryHass.entities[t.entity_id]?.name||t.entity_id;return this._t(`marker.value_badge_attr_${t.attribute}`,{name:i})}_controlCandidates(e){const t=this._markerDraft(e)?.id||e.devId||"",i=[],s=new Set;for(const e of this._markers){if(e.id===t||e.removed||e.hidden||!0!==e.is_light)continue;if("virtual"!==e.binding&&"active"!==this._bindingStatus(e.binding).kind)continue;const n=this._devices.find(t=>t.id===e.id);if(!n||n.hidden)continue;const o=this._controlRefInfo(`marker:${e.id}`);for(const e of eo(n))s.add(e);i.push({value:`marker:${e.id}`,label:o.label,sub:o.sub,icon:o.icon,search:`${o.label} ${o.sub} ${e.id} ${eo(n).join(" ")}`.toLowerCase()})}const n=Object.keys(this.hass.states||{}).filter(t=>Ms(t)&&!s.has(t)&&Xn(e.binding,[t],this._bindingEntities(e.binding)).length>0&&this._planEntityAvailable(t)).map(e=>{const t=this._controlRefInfo(e);return{value:e,label:t.label,sub:e,icon:t.icon,search:`${t.label} ${e}`.toLowerCase()}}),o=e.controlsFilter.trim().toLowerCase();return[...i,...n].filter(t=>!e.controls.includes(t.value)&&(!o||t.search.includes(o))).slice(0,12).map(({search:e,...t})=>t)}_addControlRef(e,t){if(t.startsWith("marker:")){const i=this._markerDraft(e)?.id||e.devId||"";if(!i)return void this._showToast(this._t("toast.marker_binding_required"));const s=t.slice(7),n=this._markerDraft(e);if(function(e,t,i){if(!t||t===i)return!0;const s=new Map;for(const t of e)s.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const n=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;n.has(e)||(n.add(e),o.push(...s.get(e)||[]))}return!1}(n?[...this._markers.filter(e=>e.id!==i),n]:this._markers,i,s))return void this._showToast(this._t("toast.marker_control_cycle"))}this._markerDialog=this._announceToggleDraft({...e,controls:[...e.controls,t],controlsFilter:""})}_setMarkerGlowMode(e){const t=this._markerDialog;if(!t)return;if("auto"===e)return void(this._markerDialog={...t,glowMode:e,glowTouched:!0});const i=this._markerSpatialSource(t),s=xs(i?this._planHass.states[i.eid]:void 0,null,this._fillColors.glow_light.c),n=!t.glowColorDrafted,o="fixed"===e&&!t.glowBrightnessDrafted;this._markerDialog={...t,glowMode:e,glowColor:n?s.c:t.glowColor,glowBrightness:o?Math.max(1,Math.round(100*s.bri)):t.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:t.glowBrightnessDrafted||"fixed"===e,glowTouched:!0}}_renderMarkerDialog(){const e=this._markerDialog,t="virtual"===e.bindingMode,i=this._bindingCandidates();this._bindingEntities(e.binding);const s=t?null:this._bindingStatus(e.binding),n=!t&&this._bindingHasHaPage(e.binding),o=this._markerPreviewDevice(e),a=this._effectiveMarkerTapAction(e,o),r=o?cs(this._serverCfg?.spaces.find(e=>e.id===o.space)):null,l=o?this._markerPreviewDevices(o):this._devices,c="toggle"===a&&o?this._toggleIntent(o,l):null,h=this._toggleHintLines(c),d=o?a_(this._planHass,o,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:r?.showLqi??!1!==this._config?.show_signal,designPreview:!0,activityRuntime:this._activityRt.get(o.id),lightDevices:l,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}):null,p=o?Wo(this._planHass,o,l):[],u=o?jo(this._planHass,o,p):null,_=e.valueBadgeTouched?e.valueBadgeEnabled:!!d?.valueBadge,m=e.valueBadgeTouched?e.valueBadgeSource:d?.valueBadge?.source||e.valueBadgeSource,g=e.valueBadgeTouched?e.valueBadgePosition:d?.valueBadge?.position||e.valueBadgePosition,f=Ao(m),v=!!m&&!p.some(e=>e.key===f),y=p.find(e=>e.key===f),b=d?.valueSource?d.valueSource.attribute?`attr:${d.valueSource.eid}:${d.valueSource.attribute}`:`state:${d.valueSource.eid}`:"",w=this._markerAutoHasSpatialSource(e),k=!!o&&function(e,t){return io(e,t).some(e=>!!e.eid)}(this._planHass,{...o}),x=function(e,t,i,s){const n="always"===e||"auto"===e&&t,o=n&&!i;return{sourceExists:n,fromSourceEnabled:n&&i,manualEnabled:n,radiusEnabled:n,passive:o,effectiveMode:o&&"auto"===s?"fixed":s}}(e.lightRole,w,k,e.glowMode),$=!x.sourceExists,S=!x.fromSourceEnabled,M=x.passive,C=x.effectiveMode,D="never"===e.lightRole?this._t("marker.glow_disabled_never"):"auto"!==e.lightRole||w?M?this._t("marker.glow_passive_hint"):this._t("marker.glow_disabled_no_entity"):this._t("marker.glow_disabled_auto"),P=o?eo(o):[],T=o&&to(o)||"",I=!!e.lightEntity&&!P.includes(e.lightEntity),R=o?up(o):[],A=!!e.toggleEntity&&!R.includes(e.toggleEntity),z="toggle"===a?this._toggleIntentForDialog({...e,toggleEntity:"",toggleEntityTouched:!0}):null,F=z?[...z.targets,...z.skippedTargets].map(e=>e.entityId||("ref"in e?e.ref:"")).filter(Boolean).join(", "):"",E=(()=>{if(t)return null;const s=i.find(t=>t.value===e.binding);if(s)return s.label;const[n,o]=e.binding.split(":");return"device"===n?this._fullRegistryHass.devices[o]?.name_by_user||this._fullRegistryHass.devices[o]?.name||o:this._fullRegistryHass.entities[o]?.name||this.hass.states[o]?.attributes?.friendly_name||o})();return W``}_markerValueBadgeFields(e){return(t={touched:e.valueBadgeTouched,originalHas:e.originalHasValueBadge,original:e.originalValueBadge,enabled:e.valueBadgeEnabled,source:e.valueBadgeSource,position:e.valueBadgePosition}).touched?{value_badge:{enabled:t.enabled,source:t.source,position:t.position}}:t.originalHas?{value_badge:t.original}:{};var t}_markerDraft(e){if("ha"===e.bindingMode&&(!e.binding||"virtual"===e.binding))return null;const t=qs(e.room),i=Fi(e.binding,e.devId,()=>"__hp_device_preview__"),s=this._markers.find(t=>t.id===i||t.id===e.devId),n=Zn(e.binding,e.controls,this._bindingEntities(e.binding)),o=this._effectiveStoredTapAction(e),a={id:i,binding:e.binding,name:e.name.trim()||null,icon:e.icon||null,display:"badge"!==e.display?e.display:null,ripple_color:"icon_ripple"===e.display&&e.rippleColor?e.rippleColor:null,ripple_size:"icon_ripple"===e.display&&1.5!==e.rippleSize?e.rippleSize:null,size:1!==e.size?e.size:null,angle:e.angle||null,...this._markerTapActionFields(e),...this._markerToggleEntityFields(e),tap_target:"run"===o&&e.tapTarget||null,tap_confirm:!!e.tapConfirm||null,controls:n.length?n:null,...this._markerLightFields(e),...this._markerValueBadgeFields(e),use_climate_temp:!!e.useClimateTemp||null,glow_radius_cm:(()=>{const t=Yf(e.glowRadius);return null==t||t<=0?null:Math.round(this._imperial?30.48*t:100*t)})(),model:e.model.trim()||null,link:e.link.trim()||null,description:e.description.trim()||null,pdfs:e.pdfs,hidden:e.hideFromPlan,vacuum:s?.vacuum||null};return("virtual"===e.binding||e.room)&&(a.space=t?.space||("virtual"===e.binding?this._space:null),a.area=t?.area||null,a.room_id=t?.roomId||null),a}_markerPreviewDevice(e){const t=this._markerDraft(e);if(!t)return null;const i=`${this._haRegistry.revision}\n${Md(this._markers)}\n${JSON.stringify(t)}`;if(this._markerPreviewMemo?.key===i)return this._markerPreviewMemo.device;const s=function(e){const{marker:t,siblingMarkers:i=[],...s}=e;return So({...s,markers:[...i.filter(e=>e.id!==t.id),t]}).find(e=>e.id===t.id)||null}({hass:this.hass,registry:this._haRegistry,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([e,t])=>[e,t.space])),marker:t,siblingMarkers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||this._space,loc:e=>this._t(e),iconRules:this._iconRules});return this._markerPreviewMemo={key:i,device:s},s}_markerPreviewDevices(e){const t=this._markerPreviewDevicesMemo;if(t?.base===this._devices&&t.preview===e)return t.devices;const i=[...this._devices.filter(t=>t.id!==e.id),e];return this._markerPreviewDevicesMemo={base:this._devices,preview:e,devices:i},i}_toggleIntent(e,t=this._devices){return kp({hass:this._planHass,registryHass:this._fullRegistryHass,devices:t,device:e,virtualLights:this._virtualLights})}_toggleIntentForDialog(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i=this._markerPreviewDevices(t);return this._toggleIntent(t,i)}_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 s={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 s?this._t(s):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(!$p(e)||!e.nextEffect||!e.targets.length)return[];const i="group"===e.kind,s=i?e.targets.filter(e=>"on"===e.state).length:0,n=i?0===s?t.groupAllOff():t.groupCurrent(s,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(n),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){if(!e)return[];const t=e=>this._t(`marker.toggle_effect_${e.replace("-","_")}`),i=e=>this._t(`marker.toggle_skip_${e.replace("-","_")}`);return function(e,t){const i=e.targets[0]||e.skippedTargets[0],s=[];return"group"===e.kind?e.targets.length&&s.push(t.group(e.targets)):i&&s.push(t.single(i)),e.nextEffect&&e.targets.length&&s.push("group"===e.kind?t.groupCurrentNext(e.targets,e.nextEffect):t.currentNext(e.targets[0],e.nextEffect)),e.skippedTargets.length&&s.push(t.skipped(e.skippedTargets)),!e.command&&e.noneReason&&s.push(t.none(e.noneReason)),s}(e,{single:e=>{if("via"in e&&"virtual-light"===e.via)return this._t("marker.virtual_light_target",{name:e.name});const t=e.entityId||("ref"in e?e.ref:""),i=e.name||t;return this._t("marker.toggle_hint_single",{name:i,id:t})},group:e=>this._t("marker.toggle_hint_group",{count:e.length,names:e.map(e=>`${e.name} (${e.entityId})`).join(", ")}),currentNext:(e,i)=>"virtual-light"===e.via?this._t("marker.virtual_light_current",{state:this._t("on"===e.state?"marker.virtual_light_state_on":"marker.virtual_light_state_off"),effect:t(i)}):this._t("marker.toggle_hint_current",{state:this._toggleStateText(e.entityId,e.state),effect:t(i)}),groupCurrentNext:(e,i)=>this._t("marker.toggle_hint_group_current",{on:e.filter(e=>"on"===e.state).length,count:e.length,effect:t(i)}),skipped:e=>this._t("marker.toggle_hint_skipped",{count:e.length,targets:e.map(e=>{const t=e.entityId||e.ref;return`${e.name||t} (${t}: ${i(e.reason)})`}).join(", ")}),none:e=>this._t(`marker.toggle_none_${e.replaceAll("-","_")}`)})}_effectiveStoredTapAction(e,t){return e.tapActionTouched?e.tapAction:vp(e.originalHasTapAction?e.originalTapAction:null,t)}_effectiveMarkerTapAction(e,t=this._markerPreviewDevice(e)){return this._effectiveStoredTapAction(e,t?.primary?.split(".")[0])}_announceToggleDraft(e){const t=this._markerPreviewDevice(e),i=e.tapActionTouched?e:{...e,tapAction:this._effectiveMarkerTapAction(e,t)},s="toggle"===i.tapAction?this._toggleHintLines(this._toggleIntentForDialog(i)).join(" "):"";return{...i,tapHintAnnouncement:s}}_valueBadgeForBinding(e,t){const i={...e,binding:t,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0},s=this._markerPreviewDevice(i);if(!s)return{valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgeTouched:!0};const n=[...this._devices.filter(e=>e.id!==s.id),s],o=Wo(this._planHass,s,n),a=jo(this._planHass,s,o);return{valueBadgeEnabled:e.valueBadgeEnabled&&!!a,valueBadgeSource:a,valueBadgeTouched:!0}}_markerSpatialSource(e){const t=this._markerPreviewDevice(e);if(!t)return null;const i={...t,hidden:!1};return po(ho(this._planHass,[...this._devices.filter(e=>e.id!==i.id),i],null,this._virtualLights).filter(e=>e.device.id===i.id))}_markerAutoHasSpatialSource(e){const t={...e,lightRole:"auto",lightRoleTouched:!0},i=this._markerPreviewDevice(t);return!!i&&lo(this._planHass,{...i})}_setMarkerLightRole(e){const t=this._markerDialog;t&&(this._markerDialog={...t,lightRole:e,lightRoleTouched:!0})}_controlRefInfo(e){if(!e.startsWith("marker:"))return{label:this.hass.states[e]?.attributes?.friendly_name||e,sub:e,icon:e.startsWith("light.")?"mdi:lightbulb":"mdi:toggle-switch",warning:!this._planEntityAvailable(e)};const t=e.slice(7),i=this._markers.find(e=>e.id===t),s=this._devices.find(e=>e.id===t);if(!i||i.removed||!0!==i.is_light)return{label:this._t("marker.control_missing_label"),sub:`${this._t("marker.control_broken")} (${e})`,icon:"mdi:alert-outline",warning:!0};const n=this._serverCfg?.spaces.find(e=>e.id===(s?.space||i.space)),o=!!s&&eo(s).length>0;return{label:i.name||s?.name||t,sub:[n?.title||s?.space||i.space,i.room_id||s?.area,o?"":this._t("marker.control_passive")].filter(Boolean).join(" · "),icon:i.icon||s?.icon||"mdi:lightbulb-outline",warning:!0===i.hidden||!0===s?.hidden}}_valueBadgeCandidateLabel(e){const t=e.source;if("derived_lqi"===t.kind)return this._t("marker.value_badge_lqi");if("derived_marker_state"===t.kind)return this._t("marker.value_badge_marker_state",{name:e.label});if("entity_state"===t.kind)return this._t("marker.value_badge_state",{name:e.label});const i=this.hass.states[t.entity_id]?.attributes?.friendly_name||this._fullRegistryHass.entities[t.entity_id]?.name||t.entity_id;return this._t(`marker.value_badge_attr_${t.attribute}`,{name:i})}_controlCandidates(e){const t=this._markerDraft(e)?.id||e.devId||"",i=[],s=new Set;for(const e of this._markers){if(e.id===t||e.removed||e.hidden||!0!==e.is_light)continue;if("virtual"!==e.binding&&"active"!==this._bindingStatus(e.binding).kind)continue;const n=this._devices.find(t=>t.id===e.id);if(!n||n.hidden)continue;const o=this._controlRefInfo(`marker:${e.id}`);for(const e of eo(n))s.add(e);i.push({value:`marker:${e.id}`,label:o.label,sub:o.sub,icon:o.icon,search:`${o.label} ${o.sub} ${e.id} ${eo(n).join(" ")}`.toLowerCase()})}const n=Object.keys(this.hass.states||{}).filter(t=>Ms(t)&&!s.has(t)&&Xn(e.binding,[t],this._bindingEntities(e.binding)).length>0&&this._planEntityAvailable(t)).map(e=>{const t=this._controlRefInfo(e);return{value:e,label:t.label,sub:e,icon:t.icon,search:`${t.label} ${e}`.toLowerCase()}}),o=e.controlsFilter.trim().toLowerCase();return[...i,...n].filter(t=>!e.controls.includes(t.value)&&(!o||t.search.includes(o))).slice(0,12).map(({search:e,...t})=>t)}_addControlRef(e,t){if(t.startsWith("marker:")){const i=this._markerDraft(e)?.id||e.devId||"";if(!i)return void this._showToast(this._t("toast.marker_binding_required"));const s=t.slice(7),n=this._markerDraft(e);if(function(e,t,i){if(!t||t===i)return!0;const s=new Map;for(const t of e)s.set(t.id,(t.controls||[]).filter(e=>"string"==typeof e&&e.startsWith("marker:")).map(e=>e.slice(7)));const n=new Set,o=[i];for(;o.length;){const e=o.pop();if(e===t)return!0;n.has(e)||(n.add(e),o.push(...s.get(e)||[]))}return!1}(n?[...this._markers.filter(e=>e.id!==i),n]:this._markers,i,s))return void this._showToast(this._t("toast.marker_control_cycle"))}this._markerDialog=this._announceToggleDraft({...e,controls:[...e.controls,t],controlsFilter:""})}_setMarkerGlowMode(e){const t=this._markerDialog;if(!t)return;if("auto"===e)return void(this._markerDialog={...t,glowMode:e,glowTouched:!0});const i=this._markerSpatialSource(t),s=xs(i?this._planHass.states[i.eid]:void 0,null,this._fillColors.glow_light.c),n=!t.glowColorDrafted,o="fixed"===e&&!t.glowBrightnessDrafted;this._markerDialog={...t,glowMode:e,glowColor:n?s.c:t.glowColor,glowBrightness:o?Math.max(1,Math.round(100*s.bri)):t.glowBrightness,glowColorDrafted:!0,glowBrightnessDrafted:t.glowBrightnessDrafted||"fixed"===e,glowTouched:!0}}_renderMarkerDialog(){const e=this._markerDialog,t="virtual"===e.bindingMode,i=this._bindingCandidates();this._bindingEntities(e.binding);const s=t?null:this._bindingStatus(e.binding),n=!t&&this._bindingHasHaPage(e.binding),o=this._markerPreviewDevice(e),a=this._effectiveMarkerTapAction(e,o),r=o?cs(this._serverCfg?.spaces.find(e=>e.id===o.space)):null,l=o?this._markerPreviewDevices(o):this._devices,c="toggle"===a&&o?this._toggleIntent(o,l):null,h=this._toggleHintLines(c),d=o?r_(this._planHass,o,{liveStates:!1!==this._config?.live_states,showTemperature:!1!==this._config?.show_temperature,showSignal:r?.showLqi??!1!==this._config?.show_signal,designPreview:!0,activityRuntime:this._activityRt.get(o.id),lightDevices:l,registryHass:this._fullRegistryHass,reducedMotion:this._reducedMotion}):null,p=o?Wo(this._planHass,o,l):[],u=o?jo(this._planHass,o,p):null,_=e.valueBadgeTouched?e.valueBadgeEnabled:!!d?.valueBadge,m=e.valueBadgeTouched?e.valueBadgeSource:d?.valueBadge?.source||e.valueBadgeSource,g=e.valueBadgeTouched?e.valueBadgePosition:d?.valueBadge?.position||e.valueBadgePosition,f=Ao(m),v=!!m&&!p.some(e=>e.key===f),y=p.find(e=>e.key===f),b=d?.valueSource?d.valueSource.attribute?`attr:${d.valueSource.eid}:${d.valueSource.attribute}`:`state:${d.valueSource.eid}`:"",w=this._markerAutoHasSpatialSource(e),k=!!o&&function(e,t){return io(e,t).some(e=>!!e.eid)}(this._planHass,{...o}),x=function(e,t,i,s){const n="always"===e||"auto"===e&&t,o=n&&!i;return{sourceExists:n,fromSourceEnabled:n&&i,manualEnabled:n,radiusEnabled:n,passive:o,effectiveMode:o&&"auto"===s?"fixed":s}}(e.lightRole,w,k,e.glowMode),$=!x.sourceExists,S=!x.fromSourceEnabled,M=x.passive,C=x.effectiveMode,D="never"===e.lightRole?this._t("marker.glow_disabled_never"):"auto"!==e.lightRole||w?M?this._t("marker.glow_passive_hint"):this._t("marker.glow_disabled_no_entity"):this._t("marker.glow_disabled_auto"),P=o?eo(o):[],T=o&&to(o)||"",I=!!e.lightEntity&&!P.includes(e.lightEntity),R=o?up(o):[],A=!!e.toggleEntity&&!R.includes(e.toggleEntity),z="toggle"===a?this._toggleIntentForDialog({...e,toggleEntity:"",toggleEntityTouched:!0}):null,F=z?[...z.targets,...z.skippedTargets].map(e=>e.entityId||("ref"in e?e.ref:"")).filter(Boolean).join(", "):"",E=(()=>{if(t)return null;const s=i.find(t=>t.value===e.binding);if(s)return s.label;const[n,o]=e.binding.split(":");return"device"===n?this._fullRegistryHass.devices[o]?.name_by_user||this._fullRegistryHass.devices[o]?.name||o:this._fullRegistryHass.entities[o]?.name||this.hass.states[o]?.attributes?.friendly_name||o})();return W`this._markerDialog=null}>
@@ -7097,11 +7097,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922 -

${this._t(jf[e.display])}

+

${this._t(Uf[e.display])}

${"static_icon"===e.display&&this._bindingHasAlarm(e.binding)?W`
${this._t("marker.static_alarm_warning")} @@ -7203,7 +7203,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d50bbeb80c4581235ba036af0e5e53efb922
${e.pdfs.map(e=>W` - ${e.name} + ${e.name} this._removeMarkerPdf(e.url)}>`)} `}
- `}}fv.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_tapConfirm:{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},_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},_rulesDialog:{state:!0},_settingsDialog:{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}},fv.ZOOM_MAX=8,fv.ZOOM_MIN=1/3,fv.styles=[Ke,G_],customElements.get("houseplan-card")||customElements.define("houseplan-card",fv),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${qf} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); + `}}vv.properties={_tabDrag:{state:!0},_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_continuityEpoch:{state:!0},_tapConfirm:{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},_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},_rulesDialog:{state:!0},_settingsDialog:{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}},vv.ZOOM_MAX=8,vv.ZOOM_MIN=1/3,vv.styles=[Ke,V_],customElements.get("houseplan-card")||customElements.define("houseplan-card",vv),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${Wf} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index f20b9dca..fd5c1393 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -248,8 +248,11 @@ container-type: inline-size }` + sizes in `cqw`. Legacy px values (>8) are ignor ## Device markers (v1.6.0+) Per-marker appearance: `display: badge|icon_ripple|value|static_icon`. Entity semantics originate in -`src/device-visual.ts`; `src/device-presentation.ts` resolves the complete renderer-ready -projection (sources, value, icon, classes, metrics and explanation), +`src/device-visual.ts`; `src/device-presentation.ts` resolves HA/registry/light sources and the +complete renderer-ready projection, while the pure `src/device-presentation-policy.ts` +is the single owner of lifecycle, availability, static/live/value and diagnostics +priority. Its stable internal decision trace is specified by +[`DEVICE-PRESENTATION.md`](DEVICE-PRESENTATION.md) and never enters stored config or UI. `src/device-pulse.ts` is the single pure projection from semantic activity to `none|alarm|short|continuous`, and `src/device-face.ts` renders one package-derived shell/core DOM on the full plan, diff --git a/docs/DEVICE-PRESENTATION.md b/docs/DEVICE-PRESENTATION.md new file mode 100644 index 00000000..6b69e768 --- /dev/null +++ b/docs/DEVICE-PRESENTATION.md @@ -0,0 +1,101 @@ +# Отображение маркера устройства: таблица решений + +Этот документ — developer-facing канон для issue +[#267](https://github.com/Matysh/houseplan-card/issues/267). Пользовательские +названия и обещания остаются в [USER-GUIDE.ru.md](USER-GUIDE.ru.md#12-отображение-устройств); +здесь зафиксировано, как уже разрешённые факты превращаются в одно «лицо» +маркера. Любое изменение результата требует изменения строки, fixture и +mutation evidence в одном pull request. + +## Границы ответственности + +- `device-visual.ts` классифицирует состояния сущностей. +- `device-presentation.ts` выбирает source graph и форматирует HA-данные. +- `device-presentation-policy.ts` — единственный владелец приоритета lifecycle, + availability, static/live/value и диагностических gates. +- `device-pulse.ts` — единственный владелец эффекта activity. +- `device-face.ts` только рисует готовую проекцию. +- View, static card и preview получают один `ResolvedDevicePresentation`. + +`decisionIds` — внутренний bounded trace. Он не показывается человеку, не +содержит entity IDs и не сохраняется в конфигурацию. + +## Lifecycle и видимость + +| ID | Вход | Решение | Наблюдаемый результат | Интерактивность | Доказательство | +|---|---|---|---|---|---| +| L01 | `marker.removed:true` | `pre.lifecycle.removed` | marker отсутствует в roster/DOM | отсутствует | `devices` tombstone test; `presentation-row-contract` | +| L02 | HA-disabled, View/киоск/static | `lifecycle.ha_disabled_hidden` | face скрыт, live state не участвует | отсутствует на View | `device-presentation-policy-lifecycle`; `presentation-row-contract` | +| L03 | HA-disabled, Device editor | `lifecycle.ha_disabled_hidden` | служебный ghost/preview с причиной `ha_disabled`, без live face | editor-owned, без service action | `device-presentation-policy-lifecycle`; `presentation-row-contract` | +| L04 | user-hidden, View/киоск/static | `lifecycle.user_hidden` | marker и hit-area скрыты | отсутствует | `device-presentation-policy-lifecycle`; `presentation-row-contract` | +| L05 | user-hidden, design preview | `lifecycle.user_hidden_preview` | сохранённый дизайн видим; notice `hidden_design_preview` | preview inert | `device-presentation-policy-lifecycle`; `presentation-row-contract` | +| L06 | orphaned/unverified | `lifecycle.orphaned_diagnostic` / `lifecycle.unverified_diagnostic` | нейтральная диагностическая проекция с причиной, pulse/service не оживают | surface-owned safe path | `device-presentation-policy-lifecycle`; `presentation-row-contract` | + +## Источник лица и доступность + +| ID | Вход | Решение | Наблюдаемый результат | Интерактивность | Доказательство | +|---|---|---|---|---|---| +| S01 | cover — primary/роль | `source.cover` | `sourceKind=cover`; morph с exact cover | normal surface action | `presentation-source-decision-trace`; `presentation-row-contract` | +| S02 | cover — побочная capability при light/role | `source.cover_capability_bypassed` | cover не перехватывает выбранный face | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| S03 | есть active external controls | `source.controls` + `availability.controller_available` | status берётся от целей, availability — от контроллера | normal | `controller-availability-follows-target`; `presentation-row-contract` | +| S04 | цели unavailable, controller имеет live diagnostic | `availability.controller_available` | доступная нейтральная подложка, не `unavail` | normal | `controller-diagnostics-do-not-prove-online`; `presentation-row-contract` | +| S05 | цель работает, controller не имеет live entity | `availability.controller_unavailable` | faded controller; нет yellow/pulse | normal action сохраняется | `controller-availability-follows-target`; `presentation-row-contract` | +| S06 | virtual controller + controls | `source.virtual_controller` | controller доступен; status следует цели | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| S07 | все saved controls отфильтрованы tombstone | `source.filtered_saved_controls` | controller-role сохранён, availability дают свои diagnostics | normal | `wireless-controller-loses-filtered-target-role`; `wireless-controller-preview-drops-sibling-markers` | +| S08 | manual virtual light с outgoing controls | `source.manual_virtual_light` | собственный manual source владеет face | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| S09 | owned real/forced light | `source.owned_light` | собственный light владеет face | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| S10 | есть functional device role | `source.device_role` | aggregate роли становится face | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| S11 | registry role временно отсутствует, есть primary | `source.primary_fallback` | deterministic primary fallback | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| S12 | пригодного source нет | `source.none` | neutral base-icon fallback | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| S13 | critical alarm sibling вне обычного source | `source.critical_sibling` + `status.alarm` | alarm добавлен в aggregate и побеждает normal status | normal | `device-presentation-policy-alarm`; `presentation-row-contract` | + +## Финальное лицо, контент и диагностика + +| ID | Вход | Решение | Наблюдаемый результат | Интерактивность | Доказательство | +|---|---|---|---|---|---| +| F01 | dynamic + alarm | `status.alarm` | красный alarm face | normal | `device-presentation-policy-alarm`; `presentation-row-contract` | +| F02 | dynamic + unavailable | `status.unavailable` | faded neutral plate, без pulse/hover paint | normal action сохраняется | `device-presentation-policy-unavailable`; `device-unavailable-hover-restored` | +| F03 | lock locked/unlocked | `source.device_role` + `status.neutral/open` | `lock-locked` / `lock-unlocked`, согласованный a11y state | normal | `presentation-source-decision-trace`; `presentation-row-contract` | +| F04 | working available | `status.working` | yellow plate | normal | `device-presentation-policy-status`; `presentation-row-contract` | +| F05 | open available, не cover | `status.open` | orange plate | normal | `device-presentation-policy-status`; `presentation-row-contract` | +| F06 | neutral available | `status.neutral` | theme-neutral plate | normal | `device-presentation-policy-status`; `presentation-row-contract` | +| F07 | `live_states:false`, не alarm | `face.live_states_disabled` | neutral/base icon; ordinary activity off | normal | `device-presentation-policy-live-gate`; `presentation-row-contract` | +| F08 | `static_icon` | `face.static` | neutral/base icon; state/RGB/value/metrics/pulse/vacuum off | normal | `device-presentation-policy-static`; `presentation-row-contract` | +| F09 | value + один scalar source | `content.value` | HA-formatted full Text face | normal | `device-presentation-policy-value`; `device-long-value-ellipsis-restored` | +| F10 | value + missing/unavailable/non-scalar | `content.value_fallback_icon` + `content.value_no_state/non_scalar` | icon fallback и точная причина | normal | `device-presentation-policy-value`; `presentation-row-contract` | +| F11 | value + несколько равноправных sources | `content.value_ambiguous_sources` | icon fallback `value_ambiguous_sources` | normal | `device-presentation-policy-value`; `presentation-row-contract` | +| F12 | value + virtual marker | `content.value_virtual` | icon fallback `value_virtual` | normal | `device-presentation-policy-value`; `presentation-row-contract` | +| F13 | dynamic icon + известный morph | `diagnostics.dynamic_icon` | state icon; действующее cover override сохранено | normal | `device-presentation-policy-diagnostics`; `presentation-row-contract` | +| F14 | explicit value badge | `diagnostics.value_badge` | один badge с resolved tone/position; bottom сдвигает LQI | normal | `device-presentation-policy-diagnostics`; `presentation-row-contract` | +| F15 | legacy automatic metric | `diagnostics.metrics_enabled` | прежняя temperature/humidity эвристика без записи config | normal | `device-presentation-policy-diagnostics`; `presentation-row-contract` | +| F16 | LQI 0/40, 41/179, 180+ | `diagnostics.lqi_low/mid/high` | low/mid/high и continuous canonical colour | normal | `device-marker-lqi-low-boundary-shifted`; `presentation-row-contract` | +| F17 | vacuum dynamic/static | `diagnostics.vacuum_live/vacuum_static` | live overlay только у видимого dynamic face | normal | `device-presentation-policy-diagnostics`; `presentation-row-contract` | + +## Activity и pulse + +| ID | Вход | Решение | Наблюдаемый результат | Интерактивность | Доказательство | +|---|---|---|---|---|---| +| A01 | alarm, dynamic; `live_states` любое | `pulse.alarm_alarm` | красный alarm pulse; hidden/disabled/static подавляют | normal | `device-presentation-policy-alarm`; `presentation-row-contract` | +| A02 | ordinary activity, display не `icon_ripple` | `activity.pulse_suppressed` | pulse нет; notice `activity_display_disabled` | normal | `device-presentation-policy-pulse-gate`; `presentation-row-contract` | +| A03 | witnessed event/transition, окно 3.3 s | `pulse.short_event/transition` | short pulse с generation/deadline; после deadline `none` | normal | `device-presentation-policy-pulse-gate`; `presentation-row-contract` | +| A04 | presence active | `pulse.continuous_presence` | continuous green | normal | `device-presentation-policy-pulse-gate`; `presentation-row-contract` | +| A05 | opening/closing transition | `pulse.continuous_transition` | continuous blue | normal | `device-presentation-policy-pulse-gate`; `presentation-row-contract` | +| A06 | running/working | `pulse.continuous_running` | continuous amber/live-RGB/configured | normal | `device-presentation-policy-pulse-gate`; `presentation-row-contract` | +| A07 | unavailable/hidden/disabled/static | `activity.pulse_suppressed` + `pulse.none_none` | retained runtime не рисуется | normal либо отсутствует по lifecycle | `device-presentation-policy-pulse-gate`; `presentation-row-contract` | +| A08 | reduced motion | `pulse.continuous_presence` (либо соответствующий `pulse.short_*`) | ordinary wave заменён dot; alarm остаётся красным без ordinary dot | normal | `device-presentation-policy-pulse-gate`; `presentation-row-contract` | + +## Порядок приоритетов + +1. lifecycle и effective visibility; +2. static face; +3. critical alarm; +4. live-state gate; +5. controller availability против target aggregate; +6. stable status; +7. value/icon fallback; +8. metrics, live colour и vacuum live; +9. единственный `resolveDevicePulse()`. + +Неуказанная ось не влияет на строку. Новая комбинация получает новый ряд +только тогда, когда меняет победившее решение или наблюдаемый результат; полное +декартово произведение binding × source × display × activity запрещено. diff --git a/docs/STATUS.md b/docs/STATUS.md index a64240f5..fba6ffe9 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -22,7 +22,7 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate. | Item | State | |---|---| | Version | **v1.68.1** everywhere (manifest, const.py, package.json, CARD_VERSION) — owner-approved emergency patch release for space creation and deletion (#324) | -| Current local cycle | v1.68.1 is the current stable release. Active branch `issue/264-resize-controller` is a refactor-only cycle: move Resize interaction ownership out of the root card, share the wall-record preservation invariant with the CLI, and keep user-visible behaviour unchanged. Targeted controller/invariant coverage, production Resize smokes, benchmarks and golden verification are required before review. | +| Current local cycle | v1.68.1 is the current stable release. Active branch `issue/267-device-presentation-table` is a refactor-only cycle: the marker face now has one pure ordered policy plus a canonical executable decision matrix, stable internal trace and mutation evidence. User-visible behaviour, config and renderer DOM/CSS remain unchanged. | | Hidden Labs Stage | #89 Stage 1 ships in v1.63.0-beta.1. #122 Stage 2 ships in v1.64.0 and evolves the same hidden, expiring `iso` experiment with matte walls, a low exterior floor edge, restrained shared shadows and live vertical door/window/gate panels. Flat remains default; editors and `houseplan-space-card` remain flat; live floor effects and HA actions remain unchanged. Public activation remains a separate task. | | Workflow | Superseded 2026-08-12: the pre-1.62 rule of "local edits without tests or commits" is **dead** — since release 1.62 every product change follows `PROCESS.md` (issue in `S5-ready`+, branch `issue/-slug`, trailers on every commit, review pipeline; `AGENTS.md` is the summary). Release mechanics below remain current. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Release bodies are short and bilingual (Russian first); every bullet links its GitHub issue (#NN) so the #328 rules stay machine-checkable. A STABLE body aggregates the changelog since the PREVIOUS STABLE release (never since the last beta): features/fixes described across the line's beta changelogs must appear, while bugs that were introduced and fixed strictly inside the beta line (never shipped in any stable) are excluded — draft with `npm run release:notes -- `, curate by hand, then `npm run release:notes -- --verify` must pass. `Мелкие исправления и улучшения` / `Small fixes and improvements` is allowed only when the range really contains user-visible work not itemised in the body; a single-issue hotfix ships without it (the verifier enforces this). Every body ends with separate links to the Russian and English changelogs. Open or partially delivered issues are never presented as shipped. Telegram announcements are sent only for stable releases; beta and RC publication is silent. `docs/RELEASE-NOTES.md` is the current canonical body instance; `npm run release:prerelease -- --issues=… --yes` is the primary local publication path and the manual `Publish prerelease` workflow is its GitHub-only equivalent once present on `main`. Nothing is copied to the home instance by hand | | GitHub | https://github.com/Matysh/houseplan-card — [Issues](https://github.com/Matysh/houseplan-card/issues) are the canonical task records; their labels carry priority and workflow status (`PROCESS.md` §9). GitHub Projects is no longer used. `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) | diff --git a/docs/images/screenshots.json b/docs/images/screenshots.json index 4d4481ef..43037b0f 100644 --- a/docs/images/screenshots.json +++ b/docs/images/screenshots.json @@ -2,7 +2,7 @@ "version": 1, "fixture": "synthetic-only", "chromium": "151.0.7922.34", - "sourceFingerprint": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceFingerprint": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "captureScriptSha256": "ce2e9542fed9dade3085be87d16f69adb2ac8262893ad78ad966b1b9673f2983", "command": "npm run build && node demo/docs/capture.mjs", "scenarios": { @@ -14,7 +14,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "36223106c073f07d8cc3ecf8eaab37192ebb2687daba65c5c21047d0b7890de0" }, "view-touch": { @@ -25,7 +25,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "41e3ba67f8db0e98f26f484293af83ef937c369ca5ca6a59a3350d8954c906f4" }, "space-create": { @@ -36,7 +36,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "0dc024726327f60f4a9ceaf3044381691f81f1090af81d1812e870f22d9343ba" }, "room-contour-close": { @@ -47,7 +47,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "44cfaa95bd51e2cd628400e28db0ad8b2f0cd904385845bd402494f3f5c0d93c" }, "plan-context-tray": { @@ -58,7 +58,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "e0662429b423fb74151b583dcc2c8635b001b637d03bbf7a6b16aec46399c3f8" }, "device-editor": { @@ -69,7 +69,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "d0ffd31ce80bfde21ab75da356a5fc1af38246f2b301030880320620c228d89d" }, "device-display-preview": { @@ -80,7 +80,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "2cdabae1f89c3286e4fac0ce30f757ee1690b707ab8a5488748b7cd420626160" }, "background-editor": { @@ -91,7 +91,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "30147bb00a90eea7136b4cee30995f6e6a9217b5132f3e8d3ad7471413b1af8a" }, "room-card": { @@ -102,7 +102,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea" }, "device-info": { @@ -113,7 +113,7 @@ }, "theme": "dark", "language": "en", - "sourceSha256": "d6a1d780ca6bdf4e37f6db47d1a5982efa737820cf14109f92f17308cbb5a0ec", + "sourceSha256": "fb7f3cbc180b24cb91421c5cd0b8808cd4e2d0c38dd0c6f00f46d59f94e4a6d7", "imageSha256": "dd492f53150b7149085daada5cce9eeae9bde9e7ea1d86679a54b3041f72f517" } } diff --git a/scripts/mutation-gate.mjs b/scripts/mutation-gate.mjs index 04e429fd..385f6a10 100644 --- a/scripts/mutation-gate.mjs +++ b/scripts/mutation-gate.mjs @@ -2040,10 +2040,10 @@ export const MUTANTS = [ because: 'a live controller must not inherit unavailable from its controlled lamp; ' + 'the focused matrix keeps own availability and target working as separate facts (#251)', patches: [{ - file: 'src/device-presentation.ts', - find: " let visual = controllerFace\n" - + " ? { ...combined, availability: controllerAvailability(hass, d) }", - replace: " let visual = controllerFace\n ? combined", + file: 'src/device-presentation-policy.ts', + find: " let visual = input.controllerFace\n" + + " ? { ...input.sourceVisual, availability: input.controllerAvailability }", + replace: " let visual = input.controllerFace\n ? input.sourceVisual", }], }, { @@ -2128,6 +2128,153 @@ export const MUTANTS = [ replace: ' if (lqi < 40) return \'low\';', }], }, + { + id: 'presentation-row-contract', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'a removed marker must stay outside the presentation roster; the matrix checks ' + + 'the documented pre-resolver lifecycle owner instead of pretending the face can hide it later', + patches: [{ + file: 'src/devices.ts', + find: ' if (m.removed) continue;', + replace: ' if (false && m.removed) continue;', + }], + }, + { + id: 'device-presentation-policy-lifecycle', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'HA-disabled and user-hidden markers must not leak live faces back into View; ' + + 'the pure policy test exercises both hidden output and the explicit design-preview exception', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: " if (input.bindingLifecycle === 'ha_disabled') {\n effectiveHidden = true;", + replace: " if (input.bindingLifecycle === 'ha_disabled') {\n effectiveHidden = false;", + }], + }, + { + id: 'device-presentation-policy-static', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'static_icon is the strongest visible face gate and must suppress state, value, ' + + 'metrics, pulse and vacuum live together rather than relying on renderer-specific checks', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: ' } else if (staticIcon) {\n visual = NEUTRAL_VISUAL;', + replace: ' } else if (false && staticIcon) {\n visual = NEUTRAL_VISUAL;', + }], + }, + { + id: 'device-presentation-policy-alarm', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'critical alarm must survive live_states:false for every dynamic face; removing ' + + 'the alarm exception turns the safety state into an ordinary neutral marker', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: " } else if (input.sourceVisual.status !== 'alarm' && !input.liveStates) {", + replace: ' } else if (!input.liveStates) {', + }], + }, + { + id: 'device-presentation-policy-unavailable', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'unavailable is a distinct renderer state after lifecycle and alarm; losing its ' + + 'decision lets offline markers masquerade as neutral available devices', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: " else if (visual.availability === 'unavailable') decisions.push('status.unavailable');", + replace: " else if (false && visual.availability === 'unavailable') decisions.push('status.unavailable');", + }], + }, + { + id: 'device-presentation-policy-status', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'working, open and neutral are ordered stable states; the focused table must catch ' + + 'a reordered or removed working branch before it changes the marker plate', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: " else if (visual.status === 'working') decisions.push('status.working');", + replace: " else if (false && visual.status === 'working') decisions.push('status.working');", + }], + }, + { + id: 'device-presentation-policy-live-gate', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'live_states:false must neutralise every ordinary dynamic state without suppressing ' + + 'alarm; the matrix proves the gate with observable visual output', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: " } else if (input.sourceVisual.status !== 'alarm' && !input.liveStates) {", + replace: " } else if (input.sourceVisual.status !== 'alarm' && false) {", + }], + }, + { + id: 'device-presentation-policy-value', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'value mode may render text only with one available scalar source; bypassing that ' + + 'single gate either hides valid values or revives ambiguous arbitrary registry rows', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: " const face: PresentationFace = input.display === 'value'\n" + + " && !effectiveHidden && input.valueAvailable ? 'value' : 'icon';", + replace: " const face: PresentationFace = false\n" + + " && !effectiveHidden && input.valueAvailable ? 'value' : 'icon';", + }], + }, + { + id: 'device-presentation-policy-diagnostics', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'metrics and vacuum overlays share the static/hidden gate; forcing metrics on lets ' + + 'satellites escape a static face even though the core itself remains neutral', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: ' const metrics = !staticIcon && !effectiveHidden;', + replace: ' const metrics = true;', + }], + }, + { + id: 'device-presentation-policy-pulse-gate', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'ordinary activity belongs only to icon_ripple while alarm is independent; ' + + 'dropping the display gate recreates ambiguous activity on the default badge mode', + patches: [{ + file: 'src/device-presentation-policy.ts', + find: " && visual.availability === 'available'\n" + + " && (visual.status === 'alarm' || (input.liveStates && input.display === 'icon_ripple'));", + replace: " && visual.availability === 'available'\n" + + " && (visual.status === 'alarm' || input.liveStates);", + }], + }, + { + id: 'presentation-source-decision-trace', + guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs ' + + '&& node --test --test-name-pattern="every documented decision row" ' + + 'test/device-presentation-policy.test.mjs', + because: 'source precedence must remain named and executable; erasing the cover winner must ' + + 'break the matrix even if the renderer happens to retain the same pixels for one fixture', + patches: [{ + file: 'src/device-presentation.ts', + find: " decisionIds.push('source.cover');", + replace: " decisionIds.push('source.cover_mutant');", + }], + }, { id: 'device-long-value-ellipsis-restored', guard: 'node demo/smoke_device_icon_design.mjs', diff --git a/src/device-presentation-policy.ts b/src/device-presentation-policy.ts new file mode 100644 index 00000000..763e4e33 --- /dev/null +++ b/src/device-presentation-policy.ts @@ -0,0 +1,197 @@ +import type { DeviceAvailability, DeviceVisualState } from './device-visual'; +import type { DeviceDisplayMode } from './logic'; + +export type PresentationReason = + | 'neutral' + | 'working' + | 'working_activity' + | 'open' + | 'cover_icon_state' + | 'presence' + | 'event' + | 'transition' + | 'media_neutral' + | 'unavailable' + | 'alarm' + | 'live_states_disabled' + | 'value_no_state' + | 'value_ambiguous_sources' + | 'value_non_scalar' + | 'value_virtual' + | 'vacuum_live_plan_only' + | 'hidden_design_preview' + | 'composite_power_source' + | 'activity_display_disabled' + | 'static_icon' + | 'ha_disabled' + | 'orphaned'; + +export type BindingPresentationLifecycle = + | 'active' | 'ha_disabled' | 'orphaned' | 'unverified'; + +export type PresentationFace = 'icon' | 'value'; +export type ValueFallbackReason = + | 'value_no_state' | 'value_ambiguous_sources' | 'value_non_scalar' | 'value_virtual'; + +export interface DevicePresentationPolicyInput { + bindingLifecycle: BindingPresentationLifecycle; + userHidden: boolean; + designPreview: boolean; + display: DeviceDisplayMode; + liveStates: boolean; + sourceVisual: DeviceVisualState; + controllerFace: boolean; + controllerAvailability: DeviceAvailability; + shortActivity: 'event' | 'transition' | null; + valueAvailable: boolean; + valueFallback: ValueFallbackReason | null; + vacuumLiveRequested: boolean; +} + +export interface DevicePresentationPolicyResult { + effectiveHidden: boolean; + bindingUnavailable: boolean; + visual: DeviceVisualState; + face: PresentationFace; + dynamicIcon: boolean; + metrics: boolean; + liveColor: boolean; + pulseEligible: boolean; + vacuumLive: boolean; + decisionIds: readonly string[]; +} + +const NEUTRAL_VISUAL: DeviceVisualState = { + availability: 'available', status: 'neutral', activity: 'none', +}; + +/** + * Ordered, side-effect-free policy for the final device face. + * + * HA/registry/light-graph discovery stays in device-presentation.ts. This + * function consumes only resolved facts, so every priority can be exercised + * without constructing a Home Assistant runtime or a renderer. + */ +export function resolveDevicePresentationPolicy( + input: DevicePresentationPolicyInput, +): DevicePresentationPolicyResult { + const decisions: string[] = []; + const bindingUnavailable = input.bindingLifecycle === 'ha_disabled' + || input.bindingLifecycle === 'orphaned' + || input.bindingLifecycle === 'unverified'; + + let effectiveHidden = false; + if (input.bindingLifecycle === 'ha_disabled') { + effectiveHidden = true; + decisions.push('lifecycle.ha_disabled_hidden'); + } else if (input.userHidden && !input.designPreview) { + effectiveHidden = true; + decisions.push('lifecycle.user_hidden'); + } else if (input.userHidden) { + decisions.push('lifecycle.user_hidden_preview'); + } else if (input.bindingLifecycle === 'orphaned') { + decisions.push('lifecycle.orphaned_diagnostic'); + } else if (input.bindingLifecycle === 'unverified') { + decisions.push('lifecycle.unverified_diagnostic'); + } else { + decisions.push('lifecycle.active'); + } + + const staticIcon = input.display === 'static_icon'; + let visual = input.controllerFace + ? { ...input.sourceVisual, availability: input.controllerAvailability } + : input.sourceVisual; + decisions.push(input.controllerFace + ? input.controllerAvailability === 'available' + ? 'availability.controller_available' + : 'availability.controller_unavailable' + : 'availability.source'); + + if (effectiveHidden) { + visual = NEUTRAL_VISUAL; + decisions.push('face.hidden'); + } else if (staticIcon) { + visual = NEUTRAL_VISUAL; + decisions.push('face.static'); + } else if (input.sourceVisual.status !== 'alarm' && !input.liveStates) { + visual = NEUTRAL_VISUAL; + decisions.push('face.live_states_disabled'); + } else { + decisions.push('face.dynamic'); + } + + if (!staticIcon && !effectiveHidden && input.liveStates + && input.sourceVisual.status !== 'alarm' && input.shortActivity) { + visual = { ...visual, activity: input.shortActivity }; + decisions.push(`activity.short_${input.shortActivity}`); + } + + if (visual.status === 'alarm') decisions.push('status.alarm'); + else if (visual.availability === 'unavailable') decisions.push('status.unavailable'); + else if (visual.status === 'working') decisions.push('status.working'); + else if (visual.status === 'open') decisions.push('status.open'); + else decisions.push('status.neutral'); + + const face: PresentationFace = input.display === 'value' + && !effectiveHidden && input.valueAvailable ? 'value' : 'icon'; + decisions.push(face === 'value' ? 'content.value' : input.display === 'value' + ? 'content.value_fallback_icon' : 'content.icon'); + if (input.display === 'value' && !input.valueAvailable && input.valueFallback) { + decisions.push(`content.${input.valueFallback}`); + } + + const dynamicIcon = input.liveStates && !staticIcon && !effectiveHidden; + const metrics = !staticIcon && !effectiveHidden; + const pulseEligible = !effectiveHidden && !bindingUnavailable && !staticIcon + && visual.availability === 'available' + && (visual.status === 'alarm' || (input.liveStates && input.display === 'icon_ripple')); + const vacuumLive = metrics && input.vacuumLiveRequested; + decisions.push(dynamicIcon ? 'diagnostics.dynamic_icon' : 'diagnostics.base_icon'); + decisions.push(metrics ? 'diagnostics.metrics_enabled' : 'diagnostics.metrics_suppressed'); + decisions.push(pulseEligible ? 'activity.pulse_eligible' : 'activity.pulse_suppressed'); + decisions.push(vacuumLive ? 'diagnostics.vacuum_live' : 'diagnostics.vacuum_static'); + + return { + effectiveHidden, + bindingUnavailable, + visual, + face, + dynamicIcon, + metrics, + liveColor: dynamicIcon, + pulseEligible, + vacuumLive, + decisionIds: decisions, + }; +} + +export interface PresentationReasonInput { + lifecycle: BindingPresentationLifecycle; + display: DeviceDisplayMode; + liveStates: boolean; + sourceKind: 'cover' | 'light' | 'controls' | 'device_role' | 'primary' | 'none'; + primaryDomain: string; + visual: DeviceVisualState; + activity: 'none' | 'event' | 'presence' | 'transition' | 'running'; +} + +/** The single explanation priority, kept beside the face policy it describes. */ +export function resolvePresentationReason(input: PresentationReasonInput): PresentationReason { + if (input.lifecycle === 'ha_disabled') return 'ha_disabled'; + if (input.lifecycle === 'orphaned') return 'orphaned'; + if (input.display === 'static_icon') return 'static_icon'; + if (input.visual.status === 'alarm') return 'alarm'; + if (!input.liveStates) return 'live_states_disabled'; + if (input.visual.availability === 'unavailable') return 'unavailable'; + if (input.sourceKind === 'cover') return 'cover_icon_state'; + if (input.activity === 'presence') return 'presence'; + if (input.activity === 'event') return 'event'; + if (input.activity === 'transition') return 'transition'; + if (input.visual.status === 'working') { + return input.display === 'icon_ripple' && input.activity !== 'none' + ? 'working_activity' : 'working'; + } + if (input.visual.status === 'open') return 'open'; + if (input.primaryDomain === 'media_player') return 'media_neutral'; + return 'neutral'; +} diff --git a/src/device-presentation.ts b/src/device-presentation.ts index f6ecbd0f..5e7b57e4 100644 --- a/src/device-presentation.ts +++ b/src/device-presentation.ts @@ -27,6 +27,12 @@ import { DEVICE_PULSE_DEFAULT_SCALE, resolveDevicePulse, type ResolvedDevicePulse, } from './device-pulse'; import { isManualVirtualLightMarker } from './virtual-light-state'; +import { + resolveDevicePresentationPolicy, resolvePresentationReason, + type BindingPresentationLifecycle, type PresentationReason, type ValueFallbackReason, +} from './device-presentation-policy'; + +export type { PresentationReason, ValueFallbackReason } from './device-presentation-policy'; export type PresentationSourceKind = | 'cover' | 'light' | 'controls' | 'device_role' | 'primary' | 'none'; @@ -35,9 +41,6 @@ export type PresentationSourceRole = | 'cover' | 'light' | 'control' | 'forced_light' | 'device_role' | 'lifecycle' | 'power_gate' | 'primary' | 'critical'; -export type ValueFallbackReason = - | 'value_no_state' | 'value_ambiguous_sources' | 'value_non_scalar' | 'value_virtual'; - export type DeviceLockState = 'locked' | 'unlocked' | null; export type DeviceLqiBand = 'low' | 'mid' | 'high'; export type DeviceA11yState = @@ -66,31 +69,6 @@ export function deviceA11yState(presentation: Pick< return 'neutral'; } -export type PresentationReason = - | 'neutral' - | 'working' - | 'working_activity' - | 'open' - | 'cover_icon_state' - | 'presence' - | 'event' - | 'transition' - | 'media_neutral' - | 'unavailable' - | 'alarm' - | 'live_states_disabled' - | 'value_no_state' - | 'value_ambiguous_sources' - | 'value_non_scalar' - | 'value_virtual' - | 'vacuum_live_plan_only' - | 'hidden_design_preview' - | 'composite_power_source' - | 'activity_display_disabled' - | 'static_icon' - | 'ha_disabled' - | 'orphaned'; - export interface ResolvedPresentationSource { eid: string; role: PresentationSourceRole; @@ -151,6 +129,8 @@ export interface ResolvedDevicePresentation { criticalSources: ResolvedPresentationSource[]; valueSource: ResolvedValueSource | null; sourceSignature: string; + /** Stable internal trace for the documented decision matrix. */ + decisionIds: readonly string[]; visual: DeviceVisualState; lockState: DeviceLockState; @@ -187,6 +167,7 @@ export interface ResolvedDevicePresentation { export interface ResolvedPresentationSources { sourceKind: PresentationSourceKind; + decisionIds: readonly string[]; visualSources: ResolvedPresentationSource[]; criticalSources: ResolvedPresentationSource[]; samples: EntityVisualSample[]; @@ -194,7 +175,8 @@ export interface ResolvedPresentationSources { const ACTIVITY_WINDOW_MS = 3300; const EMPTY_SOURCES: ResolvedPresentationSources = { - sourceKind: 'none', visualSources: [], criticalSources: [], samples: [], + sourceKind: 'none', decisionIds: ['source.skipped_static_fast_path'], + visualSources: [], criticalSources: [], samples: [], }; function sourceName(hass: any, eid: string): string { @@ -259,6 +241,7 @@ export function resolvePresentationSources( const d = device.hidden && device.userHidden ? { ...device, hidden: false } : device; let sourceKind: PresentationSourceKind = 'none'; let visualSources: ResolvedPresentationSource[] = []; + const decisionIds: string[] = []; // A cover remains the device's visual state source even when tap_action is // More info. Resolve the same exact cover role/capability path as the toggle @@ -331,12 +314,19 @@ export function resolvePresentationSources( || d.primary?.startsWith('cover.') || resolvedDeviceRole.some((eid) => eid.startsWith('cover.')) ); + if (cover && !coverOwnsFace) decisionIds.push('source.cover_capability_bypassed'); if (coverOwnsFace) { sourceKind = 'cover'; + decisionIds.push('source.cover'); visualSources = [sourceOf(hass, cover, 'cover')]; } else if (lights.length) { sourceKind = !manualVirtualFace && lights.some((source) => source.via === 'controls') ? 'controls' : 'light'; + if (manualVirtualFace) decisionIds.push('source.manual_virtual_light'); + else decisionIds.push(sourceKind === 'controls' ? 'source.controls' : 'source.owned_light'); + if (sourceKind === 'controls' && (d.virtual || d.bindingKind === 'virtual')) { + decisionIds.push('source.virtual_controller'); + } visualSources = lights.map((source) => { const role = source.via === 'controls' ? 'control' : source.via === 'forced' ? 'forced_light' : 'light'; @@ -367,6 +357,7 @@ export function resolvePresentationSources( : d.entities.filter((eid) => !!hass?.states?.[eid]); if (ids.length) { sourceKind = 'device_role'; + decisionIds.push('source.device_role'); // Registry metadata chooses the role, while the live hass snapshot owns // its state. Keep those authorities together for composite lifecycle // classification even during a frozen/partial registry projection. @@ -383,9 +374,16 @@ export function resolvePresentationSources( }); } else if (d.primary) { sourceKind = 'primary'; + decisionIds.push('source.primary_fallback'); visualSources = [sourceOf(hass, d.primary, 'primary')]; + } else { + decisionIds.push('source.none'); } } + if (persistedControls.length > 0 && sourceKind !== 'controls' + && sourceKind !== 'light' && sourceKind !== 'cover') { + decisionIds.push('source.filtered_saved_controls'); + } const criticalSources: ResolvedPresentationSource[] = []; for (const eid of d.entities || []) { @@ -393,8 +391,10 @@ export function resolvePresentationSources( if (sample.status !== 'alarm' || visualSources.some((source) => source.eid === eid)) continue; criticalSources.push(sourceOf(hass, eid, 'critical', sample)); } + if (criticalSources.length) decisionIds.push('source.critical_sibling'); return { sourceKind, + decisionIds, visualSources, criticalSources, samples: [...visualSources, ...criticalSources].map((source) => source.sample), @@ -543,34 +543,6 @@ export function activitySourceSignature( return signatureOf(d, sources, null).replace(/\nvalue:none$/, ''); } -function explanationReason( - d: DevItem, - visual: DeviceVisualState, - activity: DeviceActivity, - sourceKind: PresentationSourceKind, - liveStates: boolean, - haDisabled: boolean, - orphaned: boolean, - display: ResolvedDevicePresentation['display'], -): PresentationReason { - if (haDisabled) return 'ha_disabled'; - if (orphaned) return 'orphaned'; - if (display === 'static_icon') return 'static_icon'; - if (visual.status === 'alarm') return 'alarm'; - if (!liveStates) return 'live_states_disabled'; - if (visual.availability === 'unavailable') return 'unavailable'; - if (sourceKind === 'cover') return 'cover_icon_state'; - if (activity === 'presence') return 'presence'; - if (activity === 'event') return 'event'; - if (activity === 'transition') return 'transition'; - if (visual.status === 'working') { - return display === 'icon_ripple' && activity !== 'none' ? 'working_activity' : 'working'; - } - if (visual.status === 'open') return 'open'; - if ((d.primary || '').startsWith('media_player.')) return 'media_neutral'; - return 'neutral'; -} - export function presentationClasses(presentation: Pick< ResolvedDevicePresentation, 'visual' | 'lockState' | 'activity' | 'display' | 'effectiveHidden' | 'activityGeneration' | 'pulse' @@ -612,8 +584,9 @@ export function resolveDevicePresentation( const status = d.bindingStatus; const haDisabled = status?.kind === 'ha_disabled'; const orphaned = status?.kind === 'orphaned'; + const lifecycle: BindingPresentationLifecycle = haDisabled ? 'ha_disabled' + : orphaned ? 'orphaned' : status?.kind === 'unverified' ? 'unverified' : 'active'; const userHidden = d.userHidden === true || d.marker?.hidden === true; - const effectiveHidden = haDisabled || (userHidden && !options.designPreview); const combined = combineVisualSamples(sources.samples); const lockSource = sources.visualSources.find((source) => source.eid.startsWith('lock.')); const lockState: DeviceLockState = lockSource @@ -635,15 +608,6 @@ export function resolveDevicePresentation( ).length > 0; const controllerFace = sources.sourceKind === 'controls' || (configuredController && sources.sourceKind !== 'light' && sources.sourceKind !== 'cover'); - let visual = controllerFace - ? { ...combined, availability: controllerAvailability(hass, d) } - : combined; - if (effectiveHidden) visual = { availability: 'available', status: 'neutral', activity: 'none' }; - else if (staticIcon) visual = { availability: 'available', status: 'neutral', activity: 'none' }; - else if (combined.status !== 'alarm' && !options.liveStates) { - visual = { availability: 'available', status: 'neutral', activity: 'none' }; - } - const value = staticIcon && options.sourceDetails === false ? { source: null, text: null, fallback: null } : resolveValue(hass, d, sources, options.showTemperature); @@ -652,15 +616,29 @@ export function resolveDevicePresentation( const rt = options.activityRuntime; const now = options.now ?? Date.now(); const shortExpiresAt = rt?.expiresAt || (rt?.flashTs ? rt.flashTs + ACTIVITY_WINDOW_MS : 0); - if (!staticIcon && !effectiveHidden && options.liveStates && combined.status !== 'alarm' - && rt?.sources === activitySignature && rt.flashTs && rt.flashKind - && shortExpiresAt > now) { - visual = { ...visual, activity: rt.flashKind }; - } + const shortActivity = rt?.sources === activitySignature && rt.flashTs && rt.flashKind + && shortExpiresAt > now ? rt.flashKind : null; + const policy = resolveDevicePresentationPolicy({ + bindingLifecycle: lifecycle, + userHidden, + designPreview: options.designPreview === true, + display, + liveStates: options.liveStates, + sourceVisual: combined, + controllerFace, + // Preserve the ordinary-marker O(1) fast path: controller evidence is + // scanned only when this marker actually projects external controls. + controllerAvailability: controllerFace ? controllerAvailability(hass, d) : 'available', + shortActivity, + valueAvailable: value.text != null, + valueFallback: value.fallback, + vacuumLiveRequested: d.marker?.vacuum?.live === true, + }); + const { effectiveHidden, visual } = policy; const activity = display === 'icon_ripple' && !effectiveHidden && options.liveStates && visual.status !== 'alarm' ? visual.activity : 'none'; - const temp = !staticIcon && !effectiveHidden && options.showTemperature + const temp = policy.metrics && options.showTemperature ? (d.marker?.use_climate_temp === true ? climateTempFor(hass, d.entities) : (d.icon === 'mdi:thermometer' || d.icon === 'mdi:air-filter') ? tempFor(hass, d.entities) : null) : null; @@ -668,10 +646,10 @@ export function resolveDevicePresentation( // diagnostic humidity sibling must not turn a composite device into a // humidity marker; users can still select that sibling explicitly through // the configurable value badge. - const hum = !staticIcon && !effectiveHidden && options.showTemperature + const hum = policy.metrics && options.showTemperature && !!d.primary && isHumEntity(hass, d.primary) ? humFor(hass, d.entities) : null; - const lqi = !staticIcon && !effectiveHidden && options.showSignal && !d.virtual ? lqiFor(hass, d.entities) : null; + const lqi = policy.metrics && options.showSignal && !d.virtual ? lqiFor(hass, d.entities) : null; const markerStateGraph = options.lightSources || (d.marker?.value_badge?.source?.kind === 'derived_marker_state' ? resolvedLightSources(hass, options.lightDevices || [d]) : []); @@ -689,10 +667,10 @@ export function resolveDevicePresentation( ? realVisualSource?.eid : d.primary || realVisualSource?.eid; const state = actEid ? hass?.states?.[actEid] : null; - const icon = options.liveStates && !staticIcon && !effectiveHidden + const icon = policy.dynamicIcon ? stateIcon(d.icon, actEid?.split('.')[0], state?.attributes?.device_class, state?.state, !!d.marker?.icon) : d.icon; - const lightColor = options.liveStates && !staticIcon && !effectiveHidden + const lightColor = policy.liveColor ? sources.visualSources .filter((source) => !source.eid.startsWith('marker:')) .map((source) => lightColorOf(hass?.states?.[source.eid])) @@ -714,16 +692,22 @@ export function resolveDevicePresentation( now, liveStates: options.liveStates, effectiveHidden, - bindingUnavailable: haDisabled || orphaned, + bindingUnavailable: policy.bindingUnavailable, reducedMotion: options.reducedMotion, color: rippleColor, diameterScale: rippleScale, }); - const valueText = display === 'value' && !effectiveHidden ? value.text : null; + const valueText = policy.face === 'value' ? value.text : null; const disabledReason = status?.kind === 'ha_disabled' ? status.reason : null; - const reason = explanationReason( - d, visual, activity, sources.sourceKind, options.liveStates, haDisabled, orphaned, display, - ); + const reason = resolvePresentationReason({ + lifecycle, + display, + liveStates: options.liveStates, + sourceKind: sources.sourceKind, + primaryDomain: (d.primary || '').split('.')[0], + visual, + activity, + }); const notices: PresentationReason[] = []; if (options.designPreview && userHidden) notices.push('hidden_design_preview'); if (!staticIcon && d.marker?.vacuum?.live === true) notices.push('vacuum_live_plan_only'); @@ -747,6 +731,13 @@ export function resolveDevicePresentation( criticalSources: sources.criticalSources, valueSource: value.source, sourceSignature, + decisionIds: [ + ...sources.decisionIds, + ...policy.decisionIds, + ...(valueBadge ? ['diagnostics.value_badge'] : []), + ...(lqi == null ? [] : [`diagnostics.lqi_${markerLqiBand(lqi)}`]), + `pulse.${pulse.kind}_${pulse.reason}`, + ], visual, lockState, display, @@ -774,7 +765,7 @@ export function resolveDevicePresentation( haDisabled, disabledReason, orphaned, - vacuumLive: !staticIcon && d.marker?.vacuum?.live === true, + vacuumLive: policy.vacuumLive, explanation: { reason, notices }, }; return { ...presentation, classes: presentationClasses(presentation) }; diff --git a/test/device-presentation-policy.test.mjs b/test/device-presentation-policy.test.mjs new file mode 100644 index 00000000..d8034105 --- /dev/null +++ b/test/device-presentation-policy.test.mjs @@ -0,0 +1,629 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { readFileSync } from 'node:fs'; + +import { resolveDevicePresentationPolicy } from '../test-build/device-presentation-policy.js'; +import { + activitySourceSignature, markerLqiBand, resolveDevicePresentation, resolvePresentationSources, +} from '../test-build/device-presentation.js'; +import { buildDevices } from '../test-build/devices.js'; +import { DEVICE_PRESENTATION_DECISIONS } from './fixtures/device-presentation-decisions.mjs'; +import { MUTANTS } from '../scripts/mutation-gate.mjs'; + +const state = (entity_id, value, attributes = {}) => ({ entity_id, state: value, attributes }); +const hass = (states = {}, entities = {}) => ({ + states, entities, devices: {}, config: { unit_system: { temperature: '°C' } }, + formatEntityState: (item) => item.state === 'on' ? 'On' : item.state, +}); +const device = (overrides = {}) => ({ + id: 'd1', name: 'Device', model: '', area: 'room', space: 'floor', + icon: 'mdi:toggle-switch', entities: ['switch.main'], primary: 'switch.main', + bindingKind: 'device', bindingRef: 'd1', + bindingStatus: { kind: 'active', enabledEntityIds: ['switch.main'], allEntityIds: ['switch.main'] }, + marker: { id: 'd1', binding: 'device:d1' }, + ...overrides, +}); +const options = { liveStates: true, showTemperature: true, showSignal: true, now: 2_000 }; +const neutral = { availability: 'available', status: 'neutral', activity: 'none' }; + +const basePolicy = (overrides = {}) => ({ + bindingLifecycle: 'active', userHidden: false, designPreview: false, + display: 'badge', liveStates: true, sourceVisual: neutral, + controllerFace: false, controllerAvailability: 'available', shortActivity: null, + valueAvailable: false, valueFallback: null, vacuumLiveRequested: false, + ...overrides, +}); + +function assertDecision(result, decision) { + assert.ok(result.decisionIds.includes(decision), + `${decision} missing from ${result.decisionIds.join(', ')}`); +} + +const rowHass = () => hass({ + 'binary_sensor.door': state('binary_sensor.door', 'on', { device_class: 'door' }), + 'binary_sensor.presence': state('binary_sensor.presence', 'on', { device_class: 'presence' }), + 'binary_sensor.smoke': state('binary_sensor.smoke', 'on', { device_class: 'smoke' }), + 'cover.main': state('cover.main', 'opening', { device_class: 'garage' }), + 'event.button': state('event.button', 'unknown'), + 'light.main': state('light.main', 'on', { rgb_color: [12, 140, 250] }), + 'light.offline': state('light.offline', 'unavailable'), + 'light.second': state('light.second', 'off'), + 'lock.front': state('lock.front', 'locked'), + 'sensor.battery': state('sensor.battery', '100'), + 'sensor.device_linkquality': state('sensor.device_linkquality', '40'), + 'sensor.temperature': state('sensor.temperature', '22.4', { + device_class: 'temperature', unit_of_measurement: '°C', + }), + 'switch.main': state('switch.main', 'on'), + 'switch.offline': state('switch.offline', 'unavailable'), + 'vacuum.robot': state('vacuum.robot', 'cleaning'), +}, Object.fromEntries([ + ['binary_sensor.door', 'd1'], ['binary_sensor.presence', 'presence'], + ['binary_sensor.smoke', 'd1'], ['cover.main', 'd1'], ['event.button', 'd1'], + ['light.main', null], ['light.offline', null], ['light.second', null], + ['lock.front', 'd1'], ['sensor.battery', 'd1'], ['sensor.device_linkquality', 'd1'], + ['sensor.temperature', 'd1'], ['switch.main', 'd1'], ['switch.offline', 'd1'], + ['vacuum.robot', 'vacuum'], +].map(([entity_id, device_id]) => [entity_id, { + entity_id, device_id, platform: 'demo', + ...(entity_id === 'sensor.battery' || entity_id === 'sensor.device_linkquality' + ? { entity_category: 'diagnostic' } : {}), +}]))); + +const policyRow = (overrides, verify) => () => { + const result = resolveDevicePresentationPolicy(basePolicy(overrides)); + verify(result); + return result; +}; + +const sourceRow = (overrides, verify) => () => { + const result = resolvePresentationSources(rowHass(), device(overrides)); + verify(result); + return result; +}; + +const presentationRow = (overrides, optionOverrides, verify) => () => { + const result = resolveDevicePresentation( + rowHass(), device(overrides), { ...options, ...optionOverrides }, + ); + verify(result); + return result; +}; + +test('device presentation decision document, fixture and mutation registry stay exact', () => { + const doc = readFileSync(new URL('../docs/DEVICE-PRESENTATION.md', import.meta.url), 'utf8'); + const docIds = [...doc.matchAll(/^\| ([LSFA]\d{2}) \|/gm)].map((match) => match[1]); + const fixtureIds = DEVICE_PRESENTATION_DECISIONS.map((row) => row.id); + assert.deepEqual(docIds, fixtureIds); + assert.equal(new Set(fixtureIds).size, fixtureIds.length); + assert.deepEqual(fixtureIds, [...fixtureIds].sort((a, b) => + 'LSFA'.indexOf(a[0]) - 'LSFA'.indexOf(b[0]) || Number(a.slice(1)) - Number(b.slice(1)))); + + const mutantIds = new Set(MUTANTS.map((mutant) => mutant.id)); + for (const row of DEVICE_PRESENTATION_DECISIONS) { + assert.match(doc, new RegExp(`^\\| ${row.id} \\|.*\\b${row.expectedDecision.replace('.', '\\.')}`, 'm')); + assert.ok(mutantIds.has(row.mutation), `${row.id}: unknown mutation ${row.mutation}`); + } +}); + +const rowRunners = { + L01: () => { + const roster = buildDevices({ + hass: { states: {}, entities: {}, devices: {}, areas: {} }, + areaToSpace: {}, markers: [{ id: 'gone', binding: 'virtual', removed: true }], + settings: { filter_seeded: true }, excluded: new Set(), showAll: true, + firstSpaceId: 'floor', loc: (key) => key, iconRules: [], + }); + assert.deepEqual(roster, []); + return { decisionIds: ['pre.lifecycle.removed'] }; + }, + L02: policyRow({ bindingLifecycle: 'ha_disabled' }, (r) => { + assert.equal(r.effectiveHidden, true); + assert.equal(r.bindingUnavailable, true); + }), + L03: policyRow({ bindingLifecycle: 'ha_disabled', designPreview: true }, (r) => { + assert.equal(r.effectiveHidden, true); + assert.deepEqual(r.visual, neutral); + }), + L04: policyRow({ userHidden: true }, (r) => assert.equal(r.effectiveHidden, true)), + L05: policyRow({ + userHidden: true, designPreview: true, + sourceVisual: { availability: 'available', status: 'working', activity: 'running' }, + }, (r) => { + assert.equal(r.effectiveHidden, false); + assert.equal(r.visual.status, 'working'); + }), + L06: () => { + const orphaned = resolveDevicePresentationPolicy(basePolicy({ bindingLifecycle: 'orphaned' })); + const unverified = resolveDevicePresentationPolicy(basePolicy({ bindingLifecycle: 'unverified' })); + assert.equal(orphaned.bindingUnavailable, true); + assert.equal(unverified.bindingUnavailable, true); + assertDecision(unverified, 'lifecycle.unverified_diagnostic'); + return orphaned; + }, + S01: sourceRow({ entities: ['cover.main'], primary: 'cover.main', tapAction: 'info' }, + (r) => assert.equal(r.sourceKind, 'cover')), + S02: sourceRow({ + entities: ['light.main', 'cover.main'], primary: 'light.main', tapAction: 'info', + }, (r) => assert.equal(r.sourceKind, 'light')), + S03: presentationRow({ + entities: ['switch.main'], primary: 'switch.main', + marker: { id: 'd1', binding: 'device:d1', controls: ['light.main'] }, + }, {}, (r) => { + assert.equal(r.sourceKind, 'controls'); + assert.deepEqual([r.visual.availability, r.visual.status], ['available', 'working']); + }), + S04: presentationRow({ + entities: ['event.button', 'sensor.battery'], primary: 'event.button', + marker: { id: 'd1', binding: 'device:d1', controls: ['light.offline'] }, + }, {}, (r) => { + assert.equal(r.sourceKind, 'controls'); + assert.deepEqual([r.visual.availability, r.visual.status], ['available', 'neutral']); + }), + S05: presentationRow({ + entities: ['switch.offline'], primary: 'switch.offline', + marker: { id: 'd1', binding: 'device:d1', controls: ['light.main'] }, + }, {}, (r) => { + assert.deepEqual([r.visual.availability, r.visual.status], ['unavailable', 'working']); + assert.equal(r.pulse.kind, 'none'); + }), + S06: presentationRow({ + virtual: true, bindingKind: 'virtual', bindingRef: '', entities: [], primary: null, + marker: { id: 'virtual', binding: 'virtual', controls: ['light.main'] }, + }, {}, (r) => { + assert.equal(r.sourceKind, 'controls'); + assert.deepEqual([r.visual.availability, r.visual.status], ['available', 'working']); + }), + S07: presentationRow({ + entities: ['event.button', 'sensor.battery'], primary: 'event.button', controls: [], + marker: { id: 'd1', binding: 'device:d1', controls: ['light.missing'] }, + }, {}, (r) => { + assert.equal(r.sourceKind, 'device_role'); + assert.equal(r.visual.availability, 'available'); + }), + S08: sourceRow({ + id: 'manual', virtual: true, bindingKind: 'virtual', bindingRef: '', + entities: [], primary: null, + marker: { + id: 'manual', binding: 'virtual', is_light: true, tap_action: 'toggle', + controls: ['light.main'], + }, + }, (r) => assert.equal(r.sourceKind, 'light')), + S09: sourceRow({ entities: ['light.main'], primary: 'light.main' }, + (r) => assert.equal(r.sourceKind, 'light')), + S10: sourceRow({}, (r) => assert.equal(r.sourceKind, 'device_role')), + S11: sourceRow({ entities: [], primary: 'sensor.temperature' }, + (r) => assert.equal(r.sourceKind, 'primary')), + S12: sourceRow({ entities: [], primary: null }, (r) => { + assert.equal(r.sourceKind, 'none'); + assert.deepEqual(r.samples, []); + }), + S13: presentationRow({ + entities: ['switch.main', 'binary_sensor.smoke'], primary: 'switch.main', + marker: { id: 'd1', binding: 'device:d1', controls: ['light.main'] }, + }, {}, (r) => { + assert.equal(r.visual.status, 'alarm'); + assert.equal(r.criticalSources[0].eid, 'binary_sensor.smoke'); + }), + F01: policyRow({ + sourceVisual: { availability: 'available', status: 'alarm', activity: 'none' }, + }, (r) => assert.equal(r.visual.status, 'alarm')), + F02: policyRow({ + sourceVisual: { availability: 'unavailable', status: 'neutral', activity: 'none' }, + }, (r) => { + assert.equal(r.visual.availability, 'unavailable'); + assert.equal(r.pulseEligible, false); + }), + F03: () => { + const h = rowHass(); + const lock = device({ icon: 'mdi:lock', entities: ['lock.front'], primary: 'lock.front' }); + const locked = resolveDevicePresentation(h, lock, options); + h.states['lock.front'].state = 'unlocked'; + const unlocked = resolveDevicePresentation(h, lock, options); + assert.equal(locked.lockState, 'locked'); + assert.ok(locked.classes.includes('lock-locked')); + assert.equal(unlocked.lockState, 'unlocked'); + assert.ok(unlocked.classes.includes('lock-unlocked')); + return locked; + }, + F04: policyRow({ + sourceVisual: { availability: 'available', status: 'working', activity: 'running' }, + }, (r) => assert.equal(r.visual.status, 'working')), + F05: policyRow({ + sourceVisual: { availability: 'available', status: 'open', activity: 'none' }, + }, (r) => assert.equal(r.visual.status, 'open')), + F06: policyRow({}, (r) => assert.deepEqual(r.visual, neutral)), + F07: policyRow({ + liveStates: false, + sourceVisual: { availability: 'available', status: 'working', activity: 'running' }, + }, (r) => assert.deepEqual(r.visual, neutral)), + F08: policyRow({ + display: 'static_icon', valueAvailable: true, vacuumLiveRequested: true, + sourceVisual: { availability: 'available', status: 'alarm', activity: 'running' }, + }, (r) => { + assert.deepEqual(r.visual, neutral); + assert.deepEqual([r.face, r.metrics, r.vacuumLive], ['icon', false, false]); + }), + F09: presentationRow({ + marker: { id: 'd1', binding: 'device:d1', display: 'value' }, + }, {}, (r) => assert.equal(r.valueText, 'On')), + F10: presentationRow({ + entities: ['switch.offline'], primary: 'switch.offline', + marker: { id: 'd1', binding: 'device:d1', display: 'value' }, + }, {}, (r) => { + assert.equal(r.valueText, null); + assert.equal(r.fallbackReason, 'value_no_state'); + }), + F11: presentationRow({ + controls: ['light.main', 'light.second'], + marker: { + id: 'd1', binding: 'device:d1', display: 'value', + controls: ['light.main', 'light.second'], + }, + }, {}, (r) => assert.equal(r.fallbackReason, 'value_ambiguous_sources')), + F12: presentationRow({ + virtual: true, bindingKind: 'virtual', bindingRef: '', entities: [], primary: null, + marker: { id: 'virtual', binding: 'virtual', display: 'value' }, + }, {}, (r) => assert.equal(r.fallbackReason, 'value_virtual')), + F13: presentationRow({ + icon: 'mdi:garage', entities: ['cover.main'], primary: 'cover.main', tapAction: 'info', + }, {}, (r) => { + assert.equal(r.sourceKind, 'cover'); + assert.notEqual(r.icon, 'mdi:garage'); + }), + F14: presentationRow({ + icon: 'mdi:thermometer', + entities: ['sensor.temperature', 'sensor.device_linkquality'], primary: 'sensor.temperature', + marker: { + id: 'd1', binding: 'device:d1', + value_badge: { enabled: true, source: { kind: 'derived_lqi' }, position: 'bottom' }, + }, + }, {}, (r) => { + assert.equal(r.valueBadge?.text, '40'); + assert.equal(r.lqiText, null); + }), + F15: presentationRow({ + icon: 'mdi:thermometer', entities: ['sensor.temperature'], primary: 'sensor.temperature', + }, {}, (r) => assert.equal(r.tempText, '22.4')), + F16: () => { + assert.deepEqual([0, 40, 41, 179, 180].map(markerLqiBand), + ['low', 'low', 'mid', 'mid', 'high']); + return { decisionIds: ['diagnostics.lqi_low'] }; + }, + F17: presentationRow({ + id: 'vacuum', icon: 'mdi:robot-vacuum', entities: ['vacuum.robot'], primary: 'vacuum.robot', + marker: { id: 'vacuum', binding: 'device:vacuum', vacuum: { live: true } }, + }, {}, (r) => assert.equal(r.vacuumLive, true)), + A01: presentationRow({ + icon: 'mdi:smoke-detector', entities: ['binary_sensor.smoke'], primary: 'binary_sensor.smoke', + }, { liveStates: false }, (r) => assert.deepEqual([r.pulse.kind, r.pulse.reason], ['alarm', 'alarm'])), + A02: presentationRow({ + id: 'presence', icon: 'mdi:motion-sensor', entities: ['binary_sensor.presence'], + primary: 'binary_sensor.presence', + marker: { id: 'presence', binding: 'device:presence', display: 'badge' }, + }, {}, (r) => { + assert.equal(r.pulse.kind, 'none'); + assert.ok(r.explanation.notices.includes('activity_display_disabled')); + }), + A03: () => { + const h = rowHass(); + const item = device({ + id: 'presence', entities: ['binary_sensor.presence'], primary: 'binary_sensor.presence', + marker: { id: 'presence', binding: 'device:presence', display: 'icon_ripple' }, + }); + const sources = activitySourceSignature(h, item); + const live = resolveDevicePresentation(h, item, { + ...options, activityRuntime: { sources, flashTs: 1_000, flashKind: 'event', gen: 2 }, + }); + const expired = resolveDevicePresentation(h, item, { + ...options, now: 5_000, + activityRuntime: { sources, flashTs: 1_000, flashKind: 'event', gen: 2 }, + }); + assert.deepEqual([live.pulse.kind, live.pulse.reason], ['short', 'event']); + assert.notEqual(expired.pulse.kind, 'short'); + return live; + }, + A04: presentationRow({ + id: 'presence', entities: ['binary_sensor.presence'], primary: 'binary_sensor.presence', + marker: { id: 'presence', binding: 'device:presence', display: 'icon_ripple' }, + }, {}, (r) => assert.deepEqual([r.pulse.kind, r.pulse.reason], ['continuous', 'presence'])), + A05: presentationRow({ + icon: 'mdi:garage', entities: ['cover.main'], primary: 'cover.main', tapAction: 'info', + marker: { id: 'd1', binding: 'device:d1', display: 'icon_ripple' }, + }, {}, (r) => assert.deepEqual([r.pulse.kind, r.pulse.reason], ['continuous', 'transition'])), + A06: presentationRow({ + id: 'vacuum', entities: ['vacuum.robot'], primary: 'vacuum.robot', + marker: { id: 'vacuum', binding: 'device:vacuum', display: 'icon_ripple' }, + }, {}, (r) => assert.deepEqual([r.pulse.kind, r.pulse.reason], ['continuous', 'running'])), + A07: () => { + const variants = [ + resolveDevicePresentation(rowHass(), device({ + entities: ['switch.offline'], primary: 'switch.offline', + marker: { id: 'd1', binding: 'device:d1', display: 'icon_ripple' }, + }), options), + resolveDevicePresentation(rowHass(), device({ + hidden: true, userHidden: true, + marker: { id: 'd1', binding: 'device:d1', hidden: true, display: 'icon_ripple' }, + }), options), + resolveDevicePresentation(rowHass(), device({ + bindingStatus: { kind: 'orphaned', enabledEntityIds: [], allEntityIds: [] }, + entities: [], primary: null, + marker: { id: 'd1', binding: 'device:d1', display: 'icon_ripple' }, + }), options), + resolveDevicePresentation(rowHass(), device({ + marker: { id: 'd1', binding: 'device:d1', display: 'static_icon' }, + }), options), + ]; + assert.ok(variants.every((r) => r.pulse.kind === 'none')); + return variants[0]; + }, + A08: presentationRow({ + id: 'presence', entities: ['binary_sensor.presence'], primary: 'binary_sensor.presence', + marker: { id: 'presence', binding: 'device:presence', display: 'icon_ripple' }, + }, { reducedMotion: true }, (r) => { + assert.equal(r.pulse.kind, 'continuous'); + assert.equal(r.pulse.reducedMotionIndicator, 'dot'); + assert.equal(r.pulse.animated, false); + }), +}; + +test('every documented decision row executes production code and checks an observable', () => { + assert.deepEqual(Object.keys(rowRunners), DEVICE_PRESENTATION_DECISIONS.map((row) => row.id)); + for (const row of DEVICE_PRESENTATION_DECISIONS) { + const result = rowRunners[row.id](); + assertDecision(result, row.expectedDecision); + } +}); + +test('device presentation decision table rows exercise lifecycle and ordered policy output', () => { + const active = resolveDevicePresentationPolicy(basePolicy()); + assertDecision(active, 'lifecycle.active'); + assertDecision(active, 'status.neutral'); + assert.equal(active.effectiveHidden, false); + + const disabled = resolveDevicePresentationPolicy(basePolicy({ bindingLifecycle: 'ha_disabled' })); + assertDecision(disabled, 'lifecycle.ha_disabled_hidden'); + assert.equal(disabled.effectiveHidden, true); + assert.equal(disabled.pulseEligible, false); + + const hidden = resolveDevicePresentationPolicy(basePolicy({ userHidden: true })); + assertDecision(hidden, 'lifecycle.user_hidden'); + assert.equal(hidden.effectiveHidden, true); + const hiddenPreview = resolveDevicePresentationPolicy(basePolicy({ + userHidden: true, designPreview: true, + sourceVisual: { availability: 'available', status: 'working', activity: 'running' }, + })); + assertDecision(hiddenPreview, 'lifecycle.user_hidden_preview'); + assert.equal(hiddenPreview.visual.status, 'working'); + + const orphaned = resolveDevicePresentationPolicy(basePolicy({ bindingLifecycle: 'orphaned' })); + assertDecision(orphaned, 'lifecycle.orphaned_diagnostic'); + assert.equal(orphaned.bindingUnavailable, true); + const unverified = resolveDevicePresentationPolicy(basePolicy({ bindingLifecycle: 'unverified' })); + assertDecision(unverified, 'lifecycle.unverified_diagnostic'); + assert.equal(unverified.bindingUnavailable, true); + assert.equal(unverified.pulseEligible, false); + + const staticFace = resolveDevicePresentationPolicy(basePolicy({ + display: 'static_icon', valueAvailable: true, vacuumLiveRequested: true, + sourceVisual: { availability: 'available', status: 'alarm', activity: 'running' }, + })); + assertDecision(staticFace, 'face.static'); + assert.deepEqual(staticFace.visual, neutral); + assert.equal(staticFace.face, 'icon'); + assert.equal(staticFace.metrics, false); + assert.equal(staticFace.vacuumLive, false); + + const alarm = resolveDevicePresentationPolicy(basePolicy({ + liveStates: false, + sourceVisual: { availability: 'available', status: 'alarm', activity: 'none' }, + })); + assertDecision(alarm, 'status.alarm'); + assert.equal(alarm.visual.status, 'alarm'); + assert.equal(alarm.pulseEligible, true); + + const noLive = resolveDevicePresentationPolicy(basePolicy({ + liveStates: false, + sourceVisual: { availability: 'available', status: 'working', activity: 'running' }, + })); + assertDecision(noLive, 'face.live_states_disabled'); + assert.deepEqual(noLive.visual, neutral); + + for (const status of ['working', 'open', 'neutral']) { + const result = resolveDevicePresentationPolicy(basePolicy({ + sourceVisual: { availability: 'available', status, activity: 'none' }, + })); + assertDecision(result, `status.${status}`); + } + const unavailable = resolveDevicePresentationPolicy(basePolicy({ + sourceVisual: { availability: 'unavailable', status: 'neutral', activity: 'none' }, + })); + assertDecision(unavailable, 'status.unavailable'); + assert.equal(unavailable.pulseEligible, false); + + const controllerUp = resolveDevicePresentationPolicy(basePolicy({ + controllerFace: true, controllerAvailability: 'available', + sourceVisual: { availability: 'unavailable', status: 'neutral', activity: 'none' }, + })); + assertDecision(controllerUp, 'availability.controller_available'); + assert.equal(controllerUp.visual.availability, 'available'); + const controllerDown = resolveDevicePresentationPolicy(basePolicy({ + controllerFace: true, controllerAvailability: 'unavailable', + sourceVisual: { availability: 'available', status: 'working', activity: 'running' }, + })); + assertDecision(controllerDown, 'availability.controller_unavailable'); + assert.equal(controllerDown.visual.availability, 'unavailable'); + assert.equal(controllerDown.visual.status, 'working'); + + const value = resolveDevicePresentationPolicy(basePolicy({ display: 'value', valueAvailable: true })); + assertDecision(value, 'content.value'); + assert.equal(value.face, 'value'); + for (const fallback of ['value_no_state', 'value_ambiguous_sources', 'value_non_scalar', 'value_virtual']) { + const result = resolveDevicePresentationPolicy(basePolicy({ + display: 'value', valueAvailable: false, valueFallback: fallback, + })); + assert.equal(result.face, 'icon'); + assertDecision(result, `content.${fallback}`); + } + + const diagnostics = resolveDevicePresentationPolicy(basePolicy({ vacuumLiveRequested: true })); + assertDecision(diagnostics, 'diagnostics.dynamic_icon'); + assertDecision(diagnostics, 'diagnostics.metrics_enabled'); + assertDecision(diagnostics, 'diagnostics.vacuum_live'); +}); + +test('source decision trace names every source winner and controller exception', () => { + const h = hass({ + 'cover.main': state('cover.main', 'opening', { device_class: 'curtain' }), + 'light.main': state('light.main', 'on'), + 'switch.main': state('switch.main', 'on'), + 'event.button': state('event.button', 'unknown'), + 'sensor.battery': state('sensor.battery', '100'), + 'binary_sensor.smoke': state('binary_sensor.smoke', 'on', { device_class: 'smoke' }), + }, { + 'cover.main': { entity_id: 'cover.main', device_id: 'd1', platform: 'demo' }, + 'light.main': { entity_id: 'light.main', device_id: 'd1', platform: 'demo' }, + 'switch.main': { entity_id: 'switch.main', device_id: 'd1', platform: 'demo' }, + 'event.button': { entity_id: 'event.button', device_id: 'd1', platform: 'demo' }, + 'sensor.battery': { entity_id: 'sensor.battery', device_id: 'd1', platform: 'demo', entity_category: 'diagnostic' }, + 'binary_sensor.smoke': { entity_id: 'binary_sensor.smoke', device_id: 'd1', platform: 'demo' }, + }); + + const cover = resolvePresentationSources(h, device({ + entities: ['cover.main'], primary: 'cover.main', tapAction: 'info', + })); + assertDecision(cover, 'source.cover'); + assert.equal(cover.sourceKind, 'cover'); + + const mixed = resolvePresentationSources(h, device({ + entities: ['light.main', 'cover.main'], primary: 'light.main', tapAction: 'info', + })); + assertDecision(mixed, 'source.cover_capability_bypassed'); + assertDecision(mixed, 'source.owned_light'); + assert.equal(mixed.sourceKind, 'light'); + + const controlled = device({ marker: { + id: 'd1', binding: 'device:d1', controls: ['light.main'], + } }); + const controls = resolvePresentationSources(h, controlled); + assertDecision(controls, 'source.controls'); + assert.equal(controls.sourceKind, 'controls'); + + const virtual = resolvePresentationSources(h, device({ + virtual: true, bindingKind: 'virtual', entities: [], primary: null, + marker: { id: 'virtual', binding: 'virtual', controls: ['light.main'] }, + })); + assertDecision(virtual, 'source.virtual_controller'); + + const manual = resolvePresentationSources(h, device({ + id: 'manual', virtual: true, bindingKind: 'virtual', entities: [], primary: null, + marker: { + id: 'manual', binding: 'virtual', is_light: true, tap_action: 'toggle', + controls: ['light.main'], + }, + })); + assertDecision(manual, 'source.manual_virtual_light'); + + const role = resolvePresentationSources(h, device()); + assertDecision(role, 'source.device_role'); + const primary = resolvePresentationSources(h, device({ + entities: [], primary: 'sensor.battery', marker: { id: 'd1', binding: 'device:d1' }, + })); + assertDecision(primary, 'source.primary_fallback'); + const none = resolvePresentationSources(h, device({ entities: [], primary: null })); + assertDecision(none, 'source.none'); + + const filtered = resolvePresentationSources(h, device({ + entities: ['event.button', 'sensor.battery'], primary: 'event.button', + controls: [], + marker: { id: 'd1', binding: 'device:d1', controls: ['light.missing'] }, + })); + assertDecision(filtered, 'source.filtered_saved_controls'); + + const critical = resolvePresentationSources(h, device({ + entities: ['switch.main', 'binary_sensor.smoke'], primary: 'switch.main', + marker: { id: 'd1', binding: 'device:d1', controls: ['light.main'] }, + })); + assertDecision(critical, 'source.critical_sibling'); + assert.equal(critical.criticalSources[0].eid, 'binary_sensor.smoke'); +}); + +test('renderer-ready decision trace covers value, metrics, LQI, vacuum and pulse observables', () => { + const h = hass({ + 'sensor.temperature': state('sensor.temperature', '22.4', { + device_class: 'temperature', unit_of_measurement: '°C', + }), + 'sensor.device_linkquality': state('sensor.device_linkquality', '40'), + 'binary_sensor.presence': state('binary_sensor.presence', 'on', { device_class: 'presence' }), + 'vacuum.robot': state('vacuum.robot', 'cleaning'), + }, { + 'sensor.temperature': { entity_id: 'sensor.temperature', device_id: 'd1', platform: 'demo' }, + 'sensor.device_linkquality': { entity_id: 'sensor.device_linkquality', device_id: 'd1', platform: 'demo', entity_category: 'diagnostic' }, + 'binary_sensor.presence': { entity_id: 'binary_sensor.presence', device_id: 'presence', platform: 'demo' }, + 'vacuum.robot': { entity_id: 'vacuum.robot', device_id: 'vacuum', platform: 'demo' }, + }); + const temperature = resolveDevicePresentation(h, device({ + icon: 'mdi:thermometer', entities: ['sensor.temperature', 'sensor.device_linkquality'], + primary: 'sensor.temperature', + marker: { + id: 'd1', binding: 'device:d1', display: 'value', + value_badge: { enabled: true, source: { kind: 'derived_lqi' }, position: 'bottom' }, + }, + }), options); + assert.equal(temperature.valueText, '22.4°'); + assert.equal(temperature.valueBadge?.text, '40'); + assertDecision(temperature, 'content.value'); + assertDecision(temperature, 'diagnostics.value_badge'); + assertDecision(temperature, 'diagnostics.lqi_low'); + + const vacuum = resolveDevicePresentation(h, device({ + id: 'vacuum', icon: 'mdi:robot-vacuum', entities: ['vacuum.robot'], primary: 'vacuum.robot', + marker: { id: 'vacuum', binding: 'device:vacuum', vacuum: { live: true } }, + }), options); + assert.equal(vacuum.vacuumLive, true); + assertDecision(vacuum, 'diagnostics.vacuum_live'); + + const presenceDevice = device({ + id: 'presence', icon: 'mdi:motion-sensor', entities: ['binary_sensor.presence'], + primary: 'binary_sensor.presence', + marker: { id: 'presence', binding: 'device:presence', display: 'icon_ripple' }, + }); + const presence = resolveDevicePresentation(h, presenceDevice, options); + assert.equal(presence.pulse.kind, 'continuous'); + assert.equal(presence.pulse.reason, 'presence'); + assertDecision(presence, 'pulse.continuous_presence'); + + const reduced = resolveDevicePresentation(h, presenceDevice, { ...options, reducedMotion: true }); + assert.equal(reduced.pulse.reducedMotionIndicator, 'dot'); + + const noRipple = resolveDevicePresentation(h, { + ...presenceDevice, marker: { ...presenceDevice.marker, display: 'badge' }, + }, options); + assert.equal(noRipple.pulse.kind, 'none'); + assertDecision(noRipple, 'activity.pulse_suppressed'); + + const eventDevice = device({ + id: 'event', entities: ['binary_sensor.presence'], primary: 'binary_sensor.presence', + marker: { id: 'event', binding: 'device:presence', display: 'icon_ripple' }, + }); + const signature = activitySourceSignature(h, eventDevice); + const event = resolveDevicePresentation(h, eventDevice, { + ...options, + activityRuntime: { sources: signature, flashTs: 1_000, flashKind: 'event', gen: 2 }, + }); + assert.equal(event.pulse.kind, 'short'); + assertDecision(event, 'pulse.short_event'); + + const expired = resolveDevicePresentation(h, eventDevice, { + ...options, now: 5_000, + activityRuntime: { sources: signature, flashTs: 1_000, flashKind: 'event', gen: 2 }, + }); + assert.notEqual(expired.pulse.kind, 'short'); + + const sourceFiles = [ + '../src/devices.ts', '../src/houseplan-card.ts', '../src/hp-device-preview.ts', + ].map((path) => readFileSync(new URL(path, import.meta.url), 'utf8')); + assert.match(sourceFiles[0], /if \(m\.removed\) continue/); + assert.match(sourceFiles[1], /resolveDevicePresentation\(/); + assert.match(sourceFiles[2], /renderDeviceFace\(shown/); +}); diff --git a/test/fixtures/device-presentation-decisions.mjs b/test/fixtures/device-presentation-decisions.mjs new file mode 100644 index 00000000..e11586d7 --- /dev/null +++ b/test/fixtures/device-presentation-decisions.mjs @@ -0,0 +1,66 @@ +/** + * Executable index for docs/DEVICE-PRESENTATION.md. + * + * `expectedDecision` is a stable production trace fragment. `mutation` names + * the focused guard that must turn red if the owning rule is weakened. + */ +const ROWS = [ + ['L01', 'pre.lifecycle.removed', 'presentation-row-contract'], + ['L02', 'lifecycle.ha_disabled_hidden', 'device-presentation-policy-lifecycle'], + ['L03', 'lifecycle.ha_disabled_hidden', 'device-presentation-policy-lifecycle'], + ['L04', 'lifecycle.user_hidden', 'device-presentation-policy-lifecycle'], + ['L05', 'lifecycle.user_hidden_preview', 'device-presentation-policy-lifecycle'], + ['L06', 'lifecycle.orphaned_diagnostic', 'device-presentation-policy-lifecycle'], + ['S01', 'source.cover', 'presentation-source-decision-trace'], + ['S02', 'source.cover_capability_bypassed', 'presentation-source-decision-trace'], + ['S03', 'source.controls', 'controller-availability-follows-target'], + ['S04', 'availability.controller_available', 'controller-diagnostics-do-not-prove-online'], + ['S05', 'availability.controller_unavailable', 'controller-availability-follows-target'], + ['S06', 'source.virtual_controller', 'presentation-source-decision-trace'], + ['S07', 'source.filtered_saved_controls', 'wireless-controller-loses-filtered-target-role'], + ['S08', 'source.manual_virtual_light', 'presentation-source-decision-trace'], + ['S09', 'source.owned_light', 'presentation-source-decision-trace'], + ['S10', 'source.device_role', 'presentation-source-decision-trace'], + ['S11', 'source.primary_fallback', 'presentation-source-decision-trace'], + ['S12', 'source.none', 'presentation-source-decision-trace'], + ['S13', 'source.critical_sibling', 'device-presentation-policy-alarm'], + ['F01', 'status.alarm', 'device-presentation-policy-alarm'], + ['F02', 'status.unavailable', 'device-presentation-policy-unavailable'], + ['F03', 'source.device_role', 'presentation-source-decision-trace'], + ['F04', 'status.working', 'device-presentation-policy-status'], + ['F05', 'status.open', 'device-presentation-policy-status'], + ['F06', 'status.neutral', 'device-presentation-policy-status'], + ['F07', 'face.live_states_disabled', 'device-presentation-policy-live-gate'], + ['F08', 'face.static', 'device-presentation-policy-static'], + ['F09', 'content.value', 'device-presentation-policy-value'], + ['F10', 'content.value_no_state', 'device-presentation-policy-value'], + ['F11', 'content.value_ambiguous_sources', 'device-presentation-policy-value'], + ['F12', 'content.value_virtual', 'device-presentation-policy-value'], + ['F13', 'diagnostics.dynamic_icon', 'device-presentation-policy-diagnostics'], + ['F14', 'diagnostics.value_badge', 'device-presentation-policy-diagnostics'], + ['F15', 'diagnostics.metrics_enabled', 'device-presentation-policy-diagnostics'], + ['F16', 'diagnostics.lqi_low', 'device-marker-lqi-low-boundary-shifted'], + ['F17', 'diagnostics.vacuum_live', 'device-presentation-policy-diagnostics'], + ['A01', 'pulse.alarm_alarm', 'device-presentation-policy-alarm'], + ['A02', 'activity.pulse_suppressed', 'device-presentation-policy-pulse-gate'], + ['A03', 'pulse.short_event', 'device-presentation-policy-pulse-gate'], + ['A04', 'pulse.continuous_presence', 'device-presentation-policy-pulse-gate'], + ['A05', 'pulse.continuous_transition', 'device-presentation-policy-pulse-gate'], + ['A06', 'pulse.continuous_running', 'device-presentation-policy-pulse-gate'], + ['A07', 'pulse.none_none', 'device-presentation-policy-pulse-gate'], + ['A08', 'pulse.continuous_presence', 'device-presentation-policy-pulse-gate'], +]; + +/** + * The input scenario and observable projection are implemented by + * runDecisionFixture() in device-presentation-policy.test.mjs. Keeping them on + * the fixture row makes the document contract executable: a row cannot be + * added as decision-ID metadata without also adding a production call and an + * observable assertion. + */ +export const DEVICE_PRESENTATION_DECISIONS = ROWS.map(([id, expectedDecision, mutation]) => ({ + id, + scenario: `row-${id.toLowerCase()}`, + expectedDecision, + mutation, +}));